diff --git a/.eslintignore b/.eslintignore index 8ca8ab63..cbcaffc1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,9 +1,10 @@ dist build build-cli -jslib webpack.cli.js webpack.main.js webpack.renderer.js **/node_modules + +**/jest.config.js diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 31ee66bd..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "jslib"] - path = jslib - url = https://github.com/bitwarden/jslib.git - branch = master diff --git a/.prettierignore b/.prettierignore index 4842e48e..b19eb366 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,8 +3,6 @@ build build-cli dist -jslib - # External libraries / auto synced locales src/locales diff --git a/jslib b/jslib deleted file mode 160000 index 6bcadc4f..00000000 --- a/jslib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6bcadc4f408db2c150753f53a07d6f8888b6e9ff diff --git a/jslib/.gitignore b/jslib/.gitignore new file mode 100644 index 00000000..20b78636 --- /dev/null +++ b/jslib/.gitignore @@ -0,0 +1,9 @@ +.vs +.idea +node_modules +npm-debug.log +vwd.webinfo +*.crx +*.pem +dist +coverage diff --git a/jslib/.prettierrc.json b/jslib/.prettierrc.json new file mode 100644 index 00000000..de753c53 --- /dev/null +++ b/jslib/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} diff --git a/jslib/angular/jest.config.js b/jslib/angular/jest.config.js new file mode 100644 index 00000000..19d250fc --- /dev/null +++ b/jslib/angular/jest.config.js @@ -0,0 +1,17 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + name: "angular", + displayName: "angular tests", + preset: "jest-preset-angular", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/angular/package-lock.json b/jslib/angular/package-lock.json new file mode 100644 index 00000000..7b2465bf --- /dev/null +++ b/jslib/angular/package-lock.json @@ -0,0 +1,654 @@ +{ + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@angular/animations": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz", + "integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.14" + } + }, + "node_modules/@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^12.0.0 || ^13.0.0-0", + "@angular/core": "^12.0.0 || ^13.0.0-0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/common": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz", + "integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/compiler": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz", + "integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + } + }, + "node_modules/@angular/core": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz", + "integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.0.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz", + "integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz", + "integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/animations": "12.2.14", + "@angular/common": "12.2.14", + "@angular/core": "12.2.14" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz", + "integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/compiler": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14" + } + }, + "node_modules/@angular/router": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz", + "integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.14", + "@angular/core": "12.2.14", + "@angular/platform-browser": "12.2.14", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/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 + }, + "node_modules/duo_web_sdk": { + "version": "2.7.0", + "resolved": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "dependencies": { + "tslib": "~2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "dependencies": { + "tslib": "^2.0.0" + } + } + }, + "dependencies": { + "@angular/animations": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.14.tgz", + "integrity": "sha512-1BR5u32auVePvXNNP96DB2008V+Ku0OGqeZQl2h4XA9xzES/Zk5WllIJZXqRmWMRBVARfXsfb0RdMty9gcaVjA==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.2.0" + } + }, + "@angular/common": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.14.tgz", + "integrity": "sha512-ffYUYdwZETmFJw0AcWY30WsaWBhJxj/zSmFXWjgEGEGZH56zwbbNwfMZOYZ1jz4haAVxGu+TdXsOl2yMGzN7jQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.14.tgz", + "integrity": "sha512-dwmZi+n66IUzRFlGWu9mjXq170ZEsaDvlNLZzaPgs6vZTa4Kt7PWvIF/Y7TMvnVv/uqNG6kOhfmOkf6rfz1Gjg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/core": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.14.tgz", + "integrity": "sha512-dlVk7yqUHL2R/eCmM8LsWuxhEBfzg0y1zHt0UqCuFwlCoiw+IG4HFy4OlZEUw9NUEZJSv0aDv3sWqxLkvK5vvg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/forms": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.14.tgz", + "integrity": "sha512-/9/gSJUBXVRVdRnzgJnALAQZYJATuGDMkFC9ms9DEMG4PMAhe9x4if1lJjN6noz5RAom3qNuVBNWaYAPUxlcBQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.14.tgz", + "integrity": "sha512-fWcE2rnJ3ZCISa1oPfsIDV7FBZBoLFEdDuMXAiDYqDPKvF/E5U5nHrS+K4SlLAi094bMobtTOReNWl/Ienniyw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.14.tgz", + "integrity": "sha512-0NPF7mS91Tct8rBmOLZPmnLSuS4kbLHXo6eTgrg80OC0vlzBiQwGDVW4X3KncCoX9CpevaGJCdSMc+uPNsFOUQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/router": { + "version": "12.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.14.tgz", + "integrity": "sha512-yP5grSnqBvc4vNhtYdcxDgDYIebUKs5f0xyFkUJM5030UnQ0CV45tBsSxHMkQbPZucIfOuxpRy8xy5+4GizuwQ==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "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" + } + }, + "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 + }, + "duo_web_sdk": { + "version": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "from": "duo_web_sdk@git+https://github.com/duosecurity/duo_web_sdk.git" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "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" + } + }, + "rxjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", + "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "requires": { + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } + } + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "requires": { + "tslib": "^2.0.0" + } + } + } +} diff --git a/jslib/angular/package.json b/jslib/angular/package.json new file mode 100644 index 00000000..47cfa4a4 --- /dev/null +++ b/jslib/angular/package.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/jslib-angular", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + } +} diff --git a/jslib/angular/spec/test.ts b/jslib/angular/spec/test.ts new file mode 100644 index 00000000..6be6e7b8 --- /dev/null +++ b/jslib/angular/spec/test.ts @@ -0,0 +1,28 @@ +import { webcrypto } from "crypto"; +import "jest-preset-angular/setup-jest"; + +Object.defineProperty(window, "CSS", { value: null }); +Object.defineProperty(window, "getComputedStyle", { + value: () => { + return { + display: "none", + appearance: ["-webkit-appearance"], + }; + }, +}); + +Object.defineProperty(document, "doctype", { + value: "", +}); +Object.defineProperty(document.body.style, "transform", { + value: () => { + return { + enumerable: true, + configurable: true, + }; + }, +}); + +Object.defineProperty(window, "crypto", { + value: webcrypto, +}); diff --git a/jslib/angular/src/components/callout.component.html b/jslib/angular/src/components/callout.component.html new file mode 100644 index 00000000..a049d5cb --- /dev/null +++ b/jslib/angular/src/components/callout.component.html @@ -0,0 +1,35 @@ +
+

+ + {{ title }} +

+
+ {{ enforcedPolicyMessage }} +
    +
  • + {{ "policyInEffectMinComplexity" | i18n: getPasswordScoreAlertDisplay() }} +
  • +
  • + {{ "policyInEffectMinLength" | i18n: enforcedPolicyOptions?.minLength.toString() }} +
  • +
  • + {{ "policyInEffectUppercase" | i18n }} +
  • +
  • + {{ "policyInEffectLowercase" | i18n }} +
  • +
  • + {{ "policyInEffectNumbers" | i18n }} +
  • +
  • + {{ "policyInEffectSpecial" | i18n: "!@#$%^&*" }} +
  • +
+
+ +
diff --git a/jslib/angular/src/components/callout.component.ts b/jslib/angular/src/components/callout.component.ts new file mode 100644 index 00000000..a1785fe5 --- /dev/null +++ b/jslib/angular/src/components/callout.component.ts @@ -0,0 +1,78 @@ +import { Component, Input, OnInit } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions"; + +@Component({ + selector: "app-callout", + templateUrl: "callout.component.html", +}) +export class CalloutComponent implements OnInit { + @Input() type = "info"; + @Input() icon: string; + @Input() title: string; + @Input() clickable: boolean; + @Input() enforcedPolicyOptions: MasterPasswordPolicyOptions; + @Input() enforcedPolicyMessage: string; + @Input() useAlertRole = false; + + calloutStyle: string; + + constructor(private i18nService: I18nService) {} + + ngOnInit() { + this.calloutStyle = this.type; + + if (this.enforcedPolicyMessage === undefined) { + this.enforcedPolicyMessage = this.i18nService.t("masterPasswordPolicyInEffect"); + } + + if (this.type === "warning" || this.type === "danger") { + if (this.type === "danger") { + this.calloutStyle = "danger"; + } + if (this.title === undefined) { + this.title = this.i18nService.t("warning"); + } + if (this.icon === undefined) { + this.icon = "bwi-exclamation-triangle"; + } + } else if (this.type === "error") { + this.calloutStyle = "danger"; + if (this.title === undefined) { + this.title = this.i18nService.t("error"); + } + if (this.icon === undefined) { + this.icon = "bwi-error"; + } + } else if (this.type === "tip") { + this.calloutStyle = "success"; + if (this.title === undefined) { + this.title = this.i18nService.t("tip"); + } + if (this.icon === undefined) { + this.icon = "bwi-lightbulb"; + } + } + } + + getPasswordScoreAlertDisplay() { + if (this.enforcedPolicyOptions == null) { + return ""; + } + + let str: string; + switch (this.enforcedPolicyOptions.minComplexity) { + case 4: + str = this.i18nService.t("strong"); + break; + case 3: + str = this.i18nService.t("good"); + break; + default: + str = this.i18nService.t("weak"); + break; + } + return str + " (" + this.enforcedPolicyOptions.minComplexity + ")"; + } +} diff --git a/jslib/angular/src/components/environment.component.ts b/jslib/angular/src/components/environment.component.ts new file mode 100644 index 00000000..8b7689de --- /dev/null +++ b/jslib/angular/src/components/environment.component.ts @@ -0,0 +1,63 @@ +import { Directive, EventEmitter, Output } from "@angular/core"; + +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +@Directive() +export class EnvironmentComponent { + @Output() onSaved = new EventEmitter(); + + iconsUrl: string; + identityUrl: string; + apiUrl: string; + webVaultUrl: string; + notificationsUrl: string; + baseUrl: string; + showCustom = false; + + constructor( + protected platformUtilsService: PlatformUtilsService, + protected environmentService: EnvironmentService, + protected i18nService: I18nService + ) { + const urls = this.environmentService.getUrls(); + + this.baseUrl = urls.base || ""; + this.webVaultUrl = urls.webVault || ""; + this.apiUrl = urls.api || ""; + this.identityUrl = urls.identity || ""; + this.iconsUrl = urls.icons || ""; + this.notificationsUrl = urls.notifications || ""; + } + + async submit() { + const resUrls = await this.environmentService.setUrls({ + base: this.baseUrl, + api: this.apiUrl, + identity: this.identityUrl, + webVault: this.webVaultUrl, + icons: this.iconsUrl, + notifications: this.notificationsUrl, + }); + + // re-set urls since service can change them, ex: prefixing https:// + this.baseUrl = resUrls.base; + this.apiUrl = resUrls.api; + this.identityUrl = resUrls.identity; + this.webVaultUrl = resUrls.webVault; + this.iconsUrl = resUrls.icons; + this.notificationsUrl = resUrls.notifications; + + this.platformUtilsService.showToast("success", null, this.i18nService.t("environmentSaved")); + this.saved(); + } + + toggleCustom() { + this.showCustom = !this.showCustom; + } + + protected saved() { + this.onSaved.emit(); + } +} diff --git a/jslib/angular/src/components/icon.component.html b/jslib/angular/src/components/icon.component.html new file mode 100644 index 00000000..27182fbb --- /dev/null +++ b/jslib/angular/src/components/icon.component.html @@ -0,0 +1,11 @@ + diff --git a/jslib/angular/src/components/icon.component.ts b/jslib/angular/src/components/icon.component.ts new file mode 100644 index 00000000..9cb8e907 --- /dev/null +++ b/jslib/angular/src/components/icon.component.ts @@ -0,0 +1,112 @@ +import { Component, Input, OnChanges } from "@angular/core"; + +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { Utils } from "jslib-common/misc/utils"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +/** + * Provides a mapping from supported card brands to + * the filenames of icon that should be present in images/cards folder of clients. + */ +const cardIcons: Record = { + Visa: "card-visa", + Mastercard: "card-mastercard", + Amex: "card-amex", + Discover: "card-discover", + "Diners Club": "card-diners-club", + JCB: "card-jcb", + Maestro: "card-maestro", + UnionPay: "card-union-pay", +}; + +@Component({ + selector: "app-vault-icon", + templateUrl: "icon.component.html", +}) +export class IconComponent implements OnChanges { + @Input() cipher: CipherView; + icon: string; + image: string; + fallbackImage: string; + imageEnabled: boolean; + + private iconsUrl: string; + + constructor(environmentService: EnvironmentService, private stateService: StateService) { + this.iconsUrl = environmentService.getIconsUrl(); + } + + async ngOnChanges() { + // Components may be re-used when using cdk-virtual-scroll. Which puts the component in a weird state, + // to avoid this we reset all state variables. + this.image = null; + this.fallbackImage = null; + this.imageEnabled = !(await this.stateService.getDisableFavicon()); + this.load(); + } + + protected load() { + switch (this.cipher.type) { + case CipherType.Login: + this.icon = "bwi-globe"; + this.setLoginIcon(); + break; + case CipherType.SecureNote: + this.icon = "bwi-sticky-note"; + break; + case CipherType.Card: + this.icon = "bwi-credit-card"; + this.setCardIcon(); + break; + case CipherType.Identity: + this.icon = "bwi-id-card"; + break; + default: + break; + } + } + + private setLoginIcon() { + if (this.cipher.login.uri) { + let hostnameUri = this.cipher.login.uri; + let isWebsite = false; + + if (hostnameUri.indexOf("androidapp://") === 0) { + this.icon = "bwi-android"; + this.image = null; + } else if (hostnameUri.indexOf("iosapp://") === 0) { + this.icon = "bwi-apple"; + this.image = null; + } else if ( + this.imageEnabled && + hostnameUri.indexOf("://") === -1 && + hostnameUri.indexOf(".") > -1 + ) { + hostnameUri = "http://" + hostnameUri; + isWebsite = true; + } else if (this.imageEnabled) { + isWebsite = hostnameUri.indexOf("http") === 0 && hostnameUri.indexOf(".") > -1; + } + + if (this.imageEnabled && isWebsite) { + try { + this.image = this.iconsUrl + "/" + Utils.getHostname(hostnameUri) + "/icon.png"; + this.fallbackImage = "images/bwi-globe.png"; + } catch (e) { + // Ignore error since the fallback icon will be shown if image is null. + } + } + } else { + this.image = null; + } + } + + private setCardIcon() { + const brand = this.cipher.card.brand; + if (this.imageEnabled && brand in cardIcons) { + this.icon = "credit-card-icon " + cardIcons[brand]; + } + } +} diff --git a/jslib/angular/src/components/modal/dynamic-modal.component.ts b/jslib/angular/src/components/modal/dynamic-modal.component.ts new file mode 100644 index 00000000..46ad38c0 --- /dev/null +++ b/jslib/angular/src/components/modal/dynamic-modal.component.ts @@ -0,0 +1,79 @@ +import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from "@angular/cdk/a11y"; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ComponentRef, + ElementRef, + OnDestroy, + Type, + ViewChild, + ViewContainerRef, +} from "@angular/core"; + +import { ModalService } from "../../services/modal.service"; + +import { ModalRef } from "./modal.ref"; + +@Component({ + selector: "app-modal", + template: "", +}) +export class DynamicModalComponent implements AfterViewInit, OnDestroy { + componentRef: ComponentRef; + + @ViewChild("modalContent", { read: ViewContainerRef, static: true }) + modalContentRef: ViewContainerRef; + + childComponentType: Type; + setComponentParameters: (component: any) => void; + + private focusTrap: ConfigurableFocusTrap; + + constructor( + private modalService: ModalService, + private cd: ChangeDetectorRef, + private el: ElementRef, + private focusTrapFactory: ConfigurableFocusTrapFactory, + public modalRef: ModalRef + ) {} + + ngAfterViewInit() { + this.loadChildComponent(this.childComponentType); + if (this.setComponentParameters != null) { + this.setComponentParameters(this.componentRef.instance); + } + this.cd.detectChanges(); + + this.modalRef.created(this.el.nativeElement); + this.focusTrap = this.focusTrapFactory.create( + this.el.nativeElement.querySelector(".modal-dialog") + ); + if (this.el.nativeElement.querySelector("[appAutoFocus]") == null) { + this.focusTrap.focusFirstTabbableElementWhenReady(); + } + } + + loadChildComponent(componentType: Type) { + const componentFactory = this.modalService.resolveComponentFactory(componentType); + + this.modalContentRef.clear(); + this.componentRef = this.modalContentRef.createComponent(componentFactory); + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + } + this.focusTrap.destroy(); + } + + close() { + this.modalRef.close(); + } + + getFocus() { + const autoFocusEl = this.el.nativeElement.querySelector("[appAutoFocus]") as HTMLElement; + autoFocusEl?.focus(); + } +} diff --git a/jslib/angular/src/components/modal/modal-injector.ts b/jslib/angular/src/components/modal/modal-injector.ts new file mode 100644 index 00000000..188e5584 --- /dev/null +++ b/jslib/angular/src/components/modal/modal-injector.ts @@ -0,0 +1,10 @@ +import { InjectFlags, InjectionToken, Injector, Type } from "@angular/core"; + +export class ModalInjector implements Injector { + constructor(private _parentInjector: Injector, private _additionalTokens: WeakMap) {} + + get(token: Type | InjectionToken, notFoundValue?: T, flags?: InjectFlags): T; + get(token: any, notFoundValue?: any, flags?: any) { + return this._additionalTokens.get(token) ?? this._parentInjector.get(token, notFoundValue); + } +} diff --git a/jslib/angular/src/components/modal/modal.ref.ts b/jslib/angular/src/components/modal/modal.ref.ts new file mode 100644 index 00000000..a80acebb --- /dev/null +++ b/jslib/angular/src/components/modal/modal.ref.ts @@ -0,0 +1,50 @@ +import { Observable, Subject } from "rxjs"; +import { first } from "rxjs/operators"; + +export class ModalRef { + onCreated: Observable; // Modal added to the DOM. + onClose: Observable; // Initiated close. + onClosed: Observable; // Modal was closed (Remove element from DOM) + onShow: Observable; // Start showing modal + onShown: Observable; // Modal is fully visible + + private readonly _onCreated = new Subject(); + private readonly _onClose = new Subject(); + private readonly _onClosed = new Subject(); + private readonly _onShow = new Subject(); + private readonly _onShown = new Subject(); + private lastResult: any; + + constructor() { + this.onCreated = this._onCreated.asObservable(); + this.onClose = this._onClose.asObservable(); + this.onClosed = this._onClosed.asObservable(); + this.onShow = this._onShow.asObservable(); + this.onShown = this._onShow.asObservable(); + } + + show() { + this._onShow.next(); + } + + shown() { + this._onShown.next(); + } + + close(result?: any) { + this.lastResult = result; + this._onClose.next(result); + } + + closed() { + this._onClosed.next(this.lastResult); + } + + created(el: HTMLElement) { + this._onCreated.next(el); + } + + onClosedPromise(): Promise { + return this.onClosed.pipe(first()).toPromise(); + } +} diff --git a/jslib/angular/src/components/password-reprompt.component.ts b/jslib/angular/src/components/password-reprompt.component.ts new file mode 100644 index 00000000..03b40802 --- /dev/null +++ b/jslib/angular/src/components/password-reprompt.component.ts @@ -0,0 +1,41 @@ +import { Directive } from "@angular/core"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +import { ModalRef } from "./modal/modal.ref"; + +/** + * Used to verify the user's Master Password for the "Master Password Re-prompt" feature only. + * See UserVerificationComponent for any other situation where you need to verify the user's identity. + */ +@Directive() +export class PasswordRepromptComponent { + showPassword = false; + masterPassword = ""; + + constructor( + private modalRef: ModalRef, + private cryptoService: CryptoService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService + ) {} + + togglePassword() { + this.showPassword = !this.showPassword; + } + + async submit() { + if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null))) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + this.i18nService.t("invalidMasterPassword") + ); + return; + } + + this.modalRef.close(true); + } +} diff --git a/jslib/angular/src/components/toastr.component.ts b/jslib/angular/src/components/toastr.component.ts new file mode 100644 index 00000000..0e4ea4ea --- /dev/null +++ b/jslib/angular/src/components/toastr.component.ts @@ -0,0 +1,95 @@ +import { animate, state, style, transition, trigger } from "@angular/animations"; +import { CommonModule } from "@angular/common"; +import { Component, ModuleWithProviders, NgModule } from "@angular/core"; +import { + DefaultNoComponentGlobalConfig, + GlobalConfig, + Toast as BaseToast, + ToastPackage, + ToastrService, + TOAST_CONFIG, +} from "ngx-toastr"; + +@Component({ + selector: "[toast-component2]", + template: ` + +
+ +
+
+
+ {{ title }} [{{ duplicatesCount + 1 }}] +
+
+
+ {{ message }} +
+
+
+
+
+ `, + animations: [ + trigger("flyInOut", [ + state("inactive", style({ opacity: 0 })), + state("active", style({ opacity: 1 })), + state("removed", style({ opacity: 0 })), + transition("inactive => active", animate("{{ easeTime }}ms {{ easing }}")), + transition("active => removed", animate("{{ easeTime }}ms {{ easing }}")), + ]), + ], + preserveWhitespaces: false, +}) +export class BitwardenToast extends BaseToast { + constructor(protected toastrService: ToastrService, public toastPackage: ToastPackage) { + super(toastrService, toastPackage); + } +} + +export const BitwardenToastGlobalConfig: GlobalConfig = { + ...DefaultNoComponentGlobalConfig, + toastComponent: BitwardenToast, +}; + +@NgModule({ + imports: [CommonModule], + declarations: [BitwardenToast], + exports: [BitwardenToast], +}) +export class BitwardenToastModule { + static forRoot(config: Partial = {}): ModuleWithProviders { + return { + ngModule: BitwardenToastModule, + providers: [ + { + provide: TOAST_CONFIG, + useValue: { + default: BitwardenToastGlobalConfig, + config: config, + }, + }, + ], + }; + } +} diff --git a/jslib/angular/src/components/user-verification.component.html b/jslib/angular/src/components/user-verification.component.html new file mode 100644 index 00000000..b90c6074 --- /dev/null +++ b/jslib/angular/src/components/user-verification.component.html @@ -0,0 +1,46 @@ + + + + {{ "confirmIdentity" | i18n }} + + +
+ + + + + {{ "codeSent" | i18n }} + +
+ +
+ + + {{ "confirmIdentity" | i18n }} +
+
diff --git a/jslib/angular/src/components/user-verification.component.ts b/jslib/angular/src/components/user-verification.component.ts new file mode 100644 index 00000000..5504796e --- /dev/null +++ b/jslib/angular/src/components/user-verification.component.ts @@ -0,0 +1,96 @@ +import { animate, style, transition, trigger } from "@angular/animations"; +import { Component, OnInit } from "@angular/core"; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from "@angular/forms"; + +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { UserVerificationService } from "jslib-common/abstractions/userVerification.service"; +import { VerificationType } from "jslib-common/enums/verificationType"; +import { Verification } from "jslib-common/types/verification"; + +/** + * Used for general-purpose user verification throughout the app. + * Collects the user's master password, or if they are using Key Connector, prompts for an OTP via email. + * This is exposed to the parent component via the ControlValueAccessor interface (e.g. bind it to a FormControl). + * Use UserVerificationService to verify the user's input. + */ +@Component({ + selector: "app-user-verification", + templateUrl: "user-verification.component.html", + providers: [ + { + provide: NG_VALUE_ACCESSOR, + multi: true, + useExisting: UserVerificationComponent, + }, + ], + animations: [ + trigger("sent", [ + transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]), + ]), + ], +}) +export class UserVerificationComponent implements ControlValueAccessor, OnInit { + usesKeyConnector = false; + disableRequestOTP = false; + sentCode = false; + + secret = new FormControl(""); + + private onChange: (value: Verification) => void; + + constructor( + private keyConnectorService: KeyConnectorService, + private userVerificationService: UserVerificationService + ) {} + + async ngOnInit() { + this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector(); + this.processChanges(this.secret.value); + + this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret)); + } + + async requestOTP() { + if (this.usesKeyConnector) { + this.disableRequestOTP = true; + try { + await this.userVerificationService.requestOTP(); + this.sentCode = true; + } finally { + this.disableRequestOTP = false; + } + } + } + + writeValue(obj: any): void { + this.secret.setValue(obj); + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + // Not implemented + } + + setDisabledState?(isDisabled: boolean): void { + this.disableRequestOTP = isDisabled; + if (isDisabled) { + this.secret.disable(); + } else { + this.secret.enable(); + } + } + + private processChanges(secret: string) { + if (this.onChange == null) { + return; + } + + this.onChange({ + type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword, + secret: secret, + }); + } +} diff --git a/jslib/angular/src/directives/a11y-title.directive.ts b/jslib/angular/src/directives/a11y-title.directive.ts new file mode 100644 index 00000000..20e53a55 --- /dev/null +++ b/jslib/angular/src/directives/a11y-title.directive.ts @@ -0,0 +1,23 @@ +import { Directive, ElementRef, Input, Renderer2 } from "@angular/core"; + +@Directive({ + selector: "[appA11yTitle]", +}) +export class A11yTitleDirective { + @Input() set appA11yTitle(title: string) { + this.title = title; + } + + private title: string; + + constructor(private el: ElementRef, private renderer: Renderer2) {} + + ngOnInit() { + if (!this.el.nativeElement.hasAttribute("title")) { + this.renderer.setAttribute(this.el.nativeElement, "title", this.title); + } + if (!this.el.nativeElement.hasAttribute("aria-label")) { + this.renderer.setAttribute(this.el.nativeElement, "aria-label", this.title); + } + } +} diff --git a/jslib/angular/src/directives/api-action.directive.ts b/jslib/angular/src/directives/api-action.directive.ts new file mode 100644 index 00000000..3d121726 --- /dev/null +++ b/jslib/angular/src/directives/api-action.directive.ts @@ -0,0 +1,49 @@ +import { Directive, ElementRef, Input, OnChanges } from "@angular/core"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +import { ValidationService } from "../services/validation.service"; + +/** + * Provides error handling, in particular for any error returned by the server in an api call. + * Attach it to a
element and provide the name of the class property that will hold the api call promise. + * e.g. + * Any errors/rejections that occur will be intercepted and displayed as error toasts. + */ +@Directive({ + selector: "[appApiAction]", +}) +export class ApiActionDirective implements OnChanges { + @Input() appApiAction: Promise; + + constructor( + private el: ElementRef, + private validationService: ValidationService, + private logService: LogService + ) {} + + ngOnChanges(changes: any) { + if (this.appApiAction == null || this.appApiAction.then == null) { + return; + } + + this.el.nativeElement.loading = true; + + this.appApiAction.then( + (response: any) => { + this.el.nativeElement.loading = false; + }, + (e: any) => { + this.el.nativeElement.loading = false; + + if ((e as ErrorResponse).captchaRequired) { + this.logService.error("Captcha required error response: " + e.getSingleMessage()); + return; + } + this.logService?.error(`Received API exception: ${e}`); + this.validationService.showError(e); + } + ); + } +} diff --git a/jslib/angular/src/directives/autofocus.directive.ts b/jslib/angular/src/directives/autofocus.directive.ts new file mode 100644 index 00000000..5c5997fa --- /dev/null +++ b/jslib/angular/src/directives/autofocus.directive.ts @@ -0,0 +1,27 @@ +import { Directive, ElementRef, Input, NgZone } from "@angular/core"; +import { take } from "rxjs/operators"; + +import { Utils } from "jslib-common/misc/utils"; + +@Directive({ + selector: "[appAutofocus]", +}) +export class AutofocusDirective { + @Input() set appAutofocus(condition: boolean | string) { + this.autofocus = condition === "" || condition === true; + } + + private autofocus: boolean; + + constructor(private el: ElementRef, private ngZone: NgZone) {} + + ngOnInit() { + if (!Utils.isMobileBrowser && this.autofocus) { + if (this.ngZone.isStable) { + this.el.nativeElement.focus(); + } else { + this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus()); + } + } + } +} diff --git a/jslib/angular/src/directives/blur-click.directive.ts b/jslib/angular/src/directives/blur-click.directive.ts new file mode 100644 index 00000000..2fc1d367 --- /dev/null +++ b/jslib/angular/src/directives/blur-click.directive.ts @@ -0,0 +1,12 @@ +import { Directive, ElementRef, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appBlurClick]", +}) +export class BlurClickDirective { + constructor(private el: ElementRef) {} + + @HostListener("click") onClick() { + this.el.nativeElement.blur(); + } +} diff --git a/jslib/angular/src/directives/box-row.directive.ts b/jslib/angular/src/directives/box-row.directive.ts new file mode 100644 index 00000000..9ae94314 --- /dev/null +++ b/jslib/angular/src/directives/box-row.directive.ts @@ -0,0 +1,59 @@ +import { Directive, ElementRef, HostListener, OnInit } from "@angular/core"; + +@Directive({ + selector: "[appBoxRow]", +}) +export class BoxRowDirective implements OnInit { + el: HTMLElement = null; + formEls: Element[]; + + constructor(elRef: ElementRef) { + this.el = elRef.nativeElement; + } + + ngOnInit(): void { + this.formEls = Array.from( + this.el.querySelectorAll('input:not([type="hidden"]), select, textarea') + ); + this.formEls.forEach((formEl) => { + formEl.addEventListener( + "focus", + () => { + this.el.classList.add("active"); + }, + false + ); + + formEl.addEventListener( + "blur", + () => { + this.el.classList.remove("active"); + }, + false + ); + }); + } + + @HostListener("click", ["$event"]) onClick(event: Event) { + const target = event.target as HTMLElement; + if ( + target !== this.el && + !target.classList.contains("progress") && + !target.classList.contains("progress-bar") + ) { + return; + } + + if (this.formEls.length > 0) { + const formEl = this.formEls[0] as HTMLElement; + if (formEl.tagName.toLowerCase() === "input") { + const inputEl = formEl as HTMLInputElement; + if (inputEl.type != null && inputEl.type.toLowerCase() === "checkbox") { + inputEl.click(); + return; + } + } + formEl.focus(); + } + } +} diff --git a/jslib/angular/src/directives/fallback-src.directive.ts b/jslib/angular/src/directives/fallback-src.directive.ts new file mode 100644 index 00000000..11bce205 --- /dev/null +++ b/jslib/angular/src/directives/fallback-src.directive.ts @@ -0,0 +1,14 @@ +import { Directive, ElementRef, HostListener, Input } from "@angular/core"; + +@Directive({ + selector: "[appFallbackSrc]", +}) +export class FallbackSrcDirective { + @Input("appFallbackSrc") appFallbackSrc: string; + + constructor(private el: ElementRef) {} + + @HostListener("error") onError() { + this.el.nativeElement.src = this.appFallbackSrc; + } +} diff --git a/jslib/angular/src/directives/stop-click.directive.ts b/jslib/angular/src/directives/stop-click.directive.ts new file mode 100644 index 00000000..0e88dde3 --- /dev/null +++ b/jslib/angular/src/directives/stop-click.directive.ts @@ -0,0 +1,10 @@ +import { Directive, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appStopClick]", +}) +export class StopClickDirective { + @HostListener("click", ["$event"]) onClick($event: MouseEvent) { + $event.preventDefault(); + } +} diff --git a/jslib/angular/src/directives/stop-prop.directive.ts b/jslib/angular/src/directives/stop-prop.directive.ts new file mode 100644 index 00000000..8393e799 --- /dev/null +++ b/jslib/angular/src/directives/stop-prop.directive.ts @@ -0,0 +1,10 @@ +import { Directive, HostListener } from "@angular/core"; + +@Directive({ + selector: "[appStopProp]", +}) +export class StopPropDirective { + @HostListener("click", ["$event"]) onClick($event: MouseEvent) { + $event.stopPropagation(); + } +} diff --git a/jslib/angular/src/pipes/i18n.pipe.ts b/jslib/angular/src/pipes/i18n.pipe.ts new file mode 100644 index 00000000..8fc09434 --- /dev/null +++ b/jslib/angular/src/pipes/i18n.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +@Pipe({ + name: "i18n", +}) +export class I18nPipe implements PipeTransform { + constructor(private i18nService: I18nService) {} + + transform(id: string, p1?: string, p2?: string, p3?: string): string { + return this.i18nService.t(id, p1, p2, p3); + } +} diff --git a/jslib/angular/src/pipes/search-ciphers.pipe.ts b/jslib/angular/src/pipes/search-ciphers.pipe.ts new file mode 100644 index 00000000..e8bdd13e --- /dev/null +++ b/jslib/angular/src/pipes/search-ciphers.pipe.ts @@ -0,0 +1,41 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { CipherView } from "jslib-common/models/view/cipherView"; + +@Pipe({ + name: "searchCiphers", +}) +export class SearchCiphersPipe implements PipeTransform { + transform(ciphers: CipherView[], searchText: string, deleted = false): CipherView[] { + if (ciphers == null || ciphers.length === 0) { + return []; + } + + if (searchText == null || searchText.length < 2) { + return ciphers.filter((c) => { + return deleted !== c.isDeleted; + }); + } + + searchText = searchText.trim().toLowerCase(); + return ciphers.filter((c) => { + if (deleted !== c.isDeleted) { + return false; + } + if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) { + return true; + } + if (searchText.length >= 8 && c.id.startsWith(searchText)) { + return true; + } + if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(searchText) > -1) { + return true; + } + if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(searchText) > -1) { + return true; + } + + return false; + }); + } +} diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg b/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg new file mode 100644 index 00000000..8b37358b --- /dev/null +++ b/jslib/angular/src/scss/bwicons/fonts/bwi-font.svg @@ -0,0 +1,170 @@ + + + + + + +{ + "fontFamily": "bwi-font", + "designer": "Bitwarden, Inc.", + "designerURL": "", + "description": "Font generated by IcoMoon.", + "copyright": "Bitwarden, Inc.", + "majorVersion": 1, + "minorVersion": 0, + "version": "Version 1.0", + "fontId": "bwi-font", + "psName": "bwi-font", + "subFamily": "Regular", + "fullName": "bwi-font" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf b/jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf new file mode 100644 index 00000000..9b1cfde3 Binary files /dev/null and b/jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf differ diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff b/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff new file mode 100644 index 00000000..fdf10737 Binary files /dev/null and b/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff differ diff --git a/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2 b/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2 new file mode 100644 index 00000000..04cda689 Binary files /dev/null and b/jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2 differ diff --git a/jslib/angular/src/scss/bwicons/styles/style.scss b/jslib/angular/src/scss/bwicons/styles/style.scss new file mode 100644 index 00000000..e8c05f23 --- /dev/null +++ b/jslib/angular/src/scss/bwicons/styles/style.scss @@ -0,0 +1,250 @@ +$icomoon-font-family: "bwi-font" !default; +$icomoon-font-path: "~@bitwarden/jslib-angular/src/scss/bwicons/fonts/" !default; + +// New font sheet? Update the font-face information below +@font-face { + font-family: "#{$icomoon-font-family}"; + src: url($icomoon-font-path + "bwi-font.svg") format("svg"), + url($icomoon-font-path + "bwi-font.ttf") format("truetype"), + url($icomoon-font-path + "bwi-font.woff") format("woff"), + url($icomoon-font-path + "bwi-font.woff2") format("woff2"); + font-weight: normal; + font-style: normal; + font-display: block; +} + +// Base Class +.bwi { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: "#{$icomoon-font-family}" !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + display: inline-block; + /* Better Font Rendering */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Fixed Width Icons +.bwi-fw { + width: calc(18em / 14); + text-align: center; +} + +// Sizing Changes +.bwi-sm { + font-size: 0.875em; +} + +.bwi-lg { + font-size: calc(4em / 3); + line-height: calc(3em / 4); + vertical-align: -15%; +} + +.bwi-2x { + font-size: 2em; +} + +.bwi-3x { + font-size: 3em; +} + +.bwi-4x { + font-size: 4em; +} + +// Spin Animations +.bwi-spin { + animation: bwi-spin 2s infinite linear; +} + +@keyframes bwi-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} + +// List Icons +.bwi-ul { + padding-left: 0; + margin-left: calc(30em / 14); + list-style-type: none; + > li { + position: relative; + } +} + +.bwi-li { + position: absolute; + left: calc(-30em / 14); + width: calc(30em / 14); + top: calc(2em / 14); + text-align: center; + &.bwi-lg { + left: calc(-30em / 14) + calc(4em / 14); + } +} + +// Rotation +.bwi-rotate-270 { + transform: rotate(270deg); +} + +// For new icons - add their glyph name and value to the map below +$icons: ( + "save-changes": "\e988", + "browser": "\e985", + "mobile": "\e986", + "cli": "\e987", + "providers": "\e983", + "vault": "\e984", + "folder-closed-f": "\e982", + "rocket": "\e9ee", + "ellipsis-h": "\e9ef", + "ellipsis-v": "\e9f0", + "safari": "\e974", + "opera": "\e975", + "firefox": "\e976", + "edge": "\e977", + "chrome": "\e978", + "star-f": "\e979", + "arrow-circle-up": "\e97a", + "arrow-circle-right": "\e97b", + "arrow-circle-left": "\e97c", + "arrow-circle-down": "\e97d", + "undo": "\e97e", + "bolt": "\e97f", + "puzzle": "\e980", + "rss": "\e973", + "dbl-angle-left": "\e970", + "dbl-angle-right": "\e971", + "hamburger": "\e972", + "bw-folder-open-f": "\e93e", + "desktop": "\e96a", + "angle-left": "\e96b", + "user": "\e900", + "user-f": "\e901", + "key": "\e902", + "share-square": "\e903", + "hashtag": "\e904", + "clone": "\e905", + "list-alt": "\e906", + "id-card": "\e907", + "credit-card": "\e908", + "globe": "\e909", + "sticky-note": "\e90a", + "folder": "\e90b", + "lock": "\e90c", + "lock-f": "\e90d", + "generate": "\e90e", + "generate-f": "\e90f", + "cog": "\e910", + "cog-f": "\e911", + "check-circle": "\e912", + "eye": "\e913", + "pencil-square": "\e914", + "bookmark": "\e915", + "files": "\e916", + "trash": "\e917", + "plus": "\e918", + "star": "\e919", + "list": "\e91a", + "angle-right": "\e91b", + "external-link": "\e91c", + "refresh": "\e91d", + "search": "\e91f", + "filter": "\e920", + "plus-circle": "\e921", + "user-circle": "\e922", + "question-circle": "\e923", + "cogs": "\e924", + "minus-circle": "\e925", + "send": "\e926", + "send-f": "\e927", + "download": "\e928", + "pencil": "\e929", + "sign-out": "\e92a", + "share": "\e92b", + "clock": "\e92c", + "angle-down": "\e92d", + "caret-down": "\e92e", + "square": "\e92f", + "collection": "\e930", + "bank": "\e931", + "shield": "\e932", + "stop": "\e933", + "plus-square": "\e934", + "save": "\e935", + "sign-in": "\e936", + "spinner": "\e937", + "dollar": "\e939", + "check": "\e93a", + "check-square": "\e93b", + "minus-square": "\e93c", + "close": "\e93d", + "share-arrow": "\e96c", + "paperclip": "\e93f", + "bitcoin": "\e940", + "cut": "\e941", + "frown": "\e942", + "folder-open": "\e943", + "bug": "\e946", + "chain-broken": "\e947", + "dashboard": "\e948", + "envelope": "\e949", + "exclamation-circle": "\e94a", + "exclamation-triangle": "\e94b", + "caret-right": "\e94c", + "file-pdf": "\e94e", + "file-text": "\e94f", + "info-circle": "\e952", + "lightbulb": "\e953", + "link": "\e954", + "linux": "\e956", + "long-arrow-right": "\e957", + "money": "\e958", + "play": "\e959", + "reddit": "\e95a", + "refresh-tab": "\e95b", + "sitemap": "\e95c", + "sliders": "\e95d", + "tag": "\e95e", + "thumb-tack": "\e95f", + "thumbs-up": "\e960", + "unlock": "\e962", + "users": "\e963", + "wrench": "\e965", + "ban": "\e967", + "camera": "\e968", + "chevron-up": "\e969", + "eye-slash": "\e96d", + "file": "\e96e", + "paste": "\e96f", + "github": "\e950", + "facebook": "\e94d", + "paypal": "\e938", + "google": "\e951", + "linkedin": "\e955", + "discourse": "\e91e", + "twitter": "\e961", + "youtube": "\e966", + "windows": "\e964", + "apple": "\e945", + "android": "\e944", + "error": "\e981", + "numbered-list": "\e989", +); + +@each $name, $glyph in $icons { + .bwi-#{$name}:before { + content: $glyph; + } +} diff --git a/jslib/angular/src/scss/icons.scss b/jslib/angular/src/scss/icons.scss new file mode 100644 index 00000000..4d2ea459 --- /dev/null +++ b/jslib/angular/src/scss/icons.scss @@ -0,0 +1,44 @@ +$card-icons-base: "~@bitwarden/jslib-angular/src/images/cards/"; +$card-icons: ( + "visa": $card-icons-base + "visa-light.png", + "amex": $card-icons-base + "amex-light.png", + "diners-club": $card-icons-base + "diners_club-light.png", + "discover": $card-icons-base + "discover-light.png", + "jcb": $card-icons-base + "jcb-light.png", + "maestro": $card-icons-base + "maestro-light.png", + "mastercard": $card-icons-base + "mastercard-light.png", + "union-pay": $card-icons-base + "union_pay-light.png", +); + +$card-icons-dark: ( + "visa": $card-icons-base + "visa-dark.png", + "amex": $card-icons-base + "amex-dark.png", + "diners-club": $card-icons-base + "diners_club-dark.png", + "discover": $card-icons-base + "discover-dark.png", + "jcb": $card-icons-base + "jcb-dark.png", + "maestro": $card-icons-base + "maestro-dark.png", + "mastercard": $card-icons-base + "mastercard-dark.png", + "union-pay": $card-icons-base + "union_pay-dark.png", +); + +.credit-card-icon { + display: block; // Resolves the parent container being slighly to big + height: 19px; + width: 24px; + background-size: contain; + background-repeat: no-repeat; +} + +@each $name, $url in $card-icons { + .card-#{$name} { + background-image: url("#{$url}"); + } +} + +@each $theme in $dark-icon-themes { + @each $name, $url in $card-icons-dark { + .#{$theme} .card-#{$name} { + background-image: url("#{$url}"); + } + } +} diff --git a/jslib/angular/src/scss/webfonts.css b/jslib/angular/src/scss/webfonts.css new file mode 100644 index 00000000..fe9bb7e1 --- /dev/null +++ b/jslib/angular/src/scss/webfonts.css @@ -0,0 +1,89 @@ +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 300; + font-display: auto; + src: url(webfonts/Open_Sans-italic-300.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 400; + font-display: auto; + src: url(webfonts/Open_Sans-italic-400.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 600; + font-display: auto; + src: url(webfonts/Open_Sans-italic-600.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 700; + font-display: auto; + src: url(webfonts/Open_Sans-italic-700.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: italic; + font-weight: 800; + font-display: auto; + src: url(webfonts/Open_Sans-italic-800.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 300; + font-display: auto; + src: url(webfonts/Open_Sans-normal-300.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 400; + font-display: auto; + src: url(webfonts/Open_Sans-normal-400.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 600; + font-display: auto; + src: url(webfonts/Open_Sans-normal-600.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 700; + font-display: auto; + src: url(webfonts/Open_Sans-normal-700.woff) format("woff"); + unicode-range: U+0-10FFFF; +} + +@font-face { + font-family: "Open Sans"; + font-style: normal; + font-weight: 800; + font-display: auto; + src: url(webfonts/Open_Sans-normal-800.woff) format("woff"); + unicode-range: U+0-10FFFF; +} diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff new file mode 100644 index 00000000..8f8e97cf Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff new file mode 100644 index 00000000..f90b9960 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff new file mode 100644 index 00000000..c262a9c8 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff new file mode 100644 index 00000000..8526ac3d Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff b/jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff new file mode 100644 index 00000000..c389a125 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff new file mode 100644 index 00000000..9f56d335 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff new file mode 100644 index 00000000..153a1d63 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff new file mode 100644 index 00000000..8271bb4a Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff new file mode 100644 index 00000000..101c0be6 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff differ diff --git a/jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff b/jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff new file mode 100644 index 00000000..724c2e64 Binary files /dev/null and b/jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff differ diff --git a/jslib/angular/src/services/auth-guard.service.ts b/jslib/angular/src/services/auth-guard.service.ts new file mode 100644 index 00000000..8c06e438 --- /dev/null +++ b/jslib/angular/src/services/auth-guard.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from "@angular/core"; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router"; + +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; + +@Injectable() +export class AuthGuardService implements CanActivate { + constructor( + private vaultTimeoutService: VaultTimeoutService, + private router: Router, + private messagingService: MessagingService, + private keyConnectorService: KeyConnectorService, + private stateService: StateService + ) {} + + async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) { + const isAuthed = await this.stateService.getIsAuthenticated(); + if (!isAuthed) { + this.messagingService.send("authBlocked"); + return false; + } + + const locked = await this.vaultTimeoutService.isLocked(); + if (locked) { + if (routerState != null) { + this.messagingService.send("lockedUrl", { url: routerState.url }); + } + this.router.navigate(["lock"], { queryParams: { promptBiometric: true } }); + return false; + } + + if ( + !routerState.url.includes("remove-password") && + (await this.keyConnectorService.getConvertAccountRequired()) + ) { + this.router.navigate(["/remove-password"]); + return false; + } + + return true; + } +} diff --git a/jslib/angular/src/services/broadcaster.service.ts b/jslib/angular/src/services/broadcaster.service.ts new file mode 100644 index 00000000..36218405 --- /dev/null +++ b/jslib/angular/src/services/broadcaster.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from "@angular/core"; + +import { BroadcasterService as BaseBroadcasterService } from "jslib-common/services/broadcaster.service"; + +@Injectable() +export class BroadcasterService extends BaseBroadcasterService {} diff --git a/jslib/angular/src/services/jslib-services.module.ts b/jslib/angular/src/services/jslib-services.module.ts new file mode 100644 index 00000000..8d8aa85b --- /dev/null +++ b/jslib/angular/src/services/jslib-services.module.ts @@ -0,0 +1,492 @@ +import { Injector, LOCALE_ID, NgModule } from "@angular/core"; + +import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service"; +import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service"; +import { AuditService as AuditServiceAbstraction } from "jslib-common/abstractions/audit.service"; +import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service"; +import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service"; +import { CipherService as CipherServiceAbstraction } from "jslib-common/abstractions/cipher.service"; +import { CollectionService as CollectionServiceAbstraction } from "jslib-common/abstractions/collection.service"; +import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service"; +import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service"; +import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service"; +import { ExportService as ExportServiceAbstraction } from "jslib-common/abstractions/export.service"; +import { FileUploadService as FileUploadServiceAbstraction } from "jslib-common/abstractions/fileUpload.service"; +import { FolderService as FolderServiceAbstraction } from "jslib-common/abstractions/folder.service"; +import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service"; +import { KeyConnectorService as KeyConnectorServiceAbstraction } from "jslib-common/abstractions/keyConnector.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service"; +import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service"; +import { OrganizationService as OrganizationServiceAbstraction } from "jslib-common/abstractions/organization.service"; +import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "jslib-common/abstractions/passwordGeneration.service"; +import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; +import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service"; +import { PolicyService as PolicyServiceAbstraction } from "jslib-common/abstractions/policy.service"; +import { ProviderService as ProviderServiceAbstraction } from "jslib-common/abstractions/provider.service"; +import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service"; +import { SendService as SendServiceAbstraction } from "jslib-common/abstractions/send.service"; +import { SettingsService as SettingsServiceAbstraction } from "jslib-common/abstractions/settings.service"; +import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; +import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service"; +import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; +import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service"; +import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service"; +import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service"; +import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service"; +import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.service"; +import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "jslib-common/abstractions/usernameGeneration.service"; +import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; +import { StateFactory } from "jslib-common/factories/stateFactory"; +import { Account } from "jslib-common/models/domain/account"; +import { GlobalState } from "jslib-common/models/domain/globalState"; +import { ApiService } from "jslib-common/services/api.service"; +import { AppIdService } from "jslib-common/services/appId.service"; +import { AuditService } from "jslib-common/services/audit.service"; +import { AuthService } from "jslib-common/services/auth.service"; +import { CipherService } from "jslib-common/services/cipher.service"; +import { CollectionService } from "jslib-common/services/collection.service"; +import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; +import { CryptoService } from "jslib-common/services/crypto.service"; +import { EnvironmentService } from "jslib-common/services/environment.service"; +import { EventService } from "jslib-common/services/event.service"; +import { ExportService } from "jslib-common/services/export.service"; +import { FileUploadService } from "jslib-common/services/fileUpload.service"; +import { FolderService } from "jslib-common/services/folder.service"; +import { KeyConnectorService } from "jslib-common/services/keyConnector.service"; +import { NotificationsService } from "jslib-common/services/notifications.service"; +import { OrganizationService } from "jslib-common/services/organization.service"; +import { PasswordGenerationService } from "jslib-common/services/passwordGeneration.service"; +import { PolicyService } from "jslib-common/services/policy.service"; +import { ProviderService } from "jslib-common/services/provider.service"; +import { SearchService } from "jslib-common/services/search.service"; +import { SendService } from "jslib-common/services/send.service"; +import { SettingsService } from "jslib-common/services/settings.service"; +import { StateService } from "jslib-common/services/state.service"; +import { StateMigrationService } from "jslib-common/services/stateMigration.service"; +import { SyncService } from "jslib-common/services/sync.service"; +import { TokenService } from "jslib-common/services/token.service"; +import { TotpService } from "jslib-common/services/totp.service"; +import { TwoFactorService } from "jslib-common/services/twoFactor.service"; +import { UserVerificationService } from "jslib-common/services/userVerification.service"; +import { UsernameGenerationService } from "jslib-common/services/usernameGeneration.service"; +import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service"; +import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; + +import { AuthGuardService } from "./auth-guard.service"; +import { BroadcasterService } from "./broadcaster.service"; +import { LockGuardService } from "./lock-guard.service"; +import { ModalService } from "./modal.service"; +import { PasswordRepromptService } from "./passwordReprompt.service"; +import { UnauthGuardService } from "./unauth-guard.service"; +import { ValidationService } from "./validation.service"; + +@NgModule({ + declarations: [], + providers: [ + { provide: "WINDOW", useValue: window }, + { + provide: LOCALE_ID, + useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale, + deps: [I18nServiceAbstraction], + }, + ValidationService, + AuthGuardService, + UnauthGuardService, + LockGuardService, + ModalService, + { + provide: AppIdServiceAbstraction, + useClass: AppIdService, + deps: [StorageServiceAbstraction], + }, + { + provide: AuditServiceAbstraction, + useClass: AuditService, + deps: [CryptoFunctionServiceAbstraction, ApiServiceAbstraction], + }, + { + provide: AuthServiceAbstraction, + useClass: AuthService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + TokenServiceAbstraction, + AppIdServiceAbstraction, + PlatformUtilsServiceAbstraction, + MessagingServiceAbstraction, + LogService, + KeyConnectorServiceAbstraction, + EnvironmentServiceAbstraction, + StateServiceAbstraction, + TwoFactorServiceAbstraction, + I18nServiceAbstraction, + ], + }, + { + provide: CipherServiceAbstraction, + useFactory: ( + cryptoService: CryptoServiceAbstraction, + settingsService: SettingsServiceAbstraction, + apiService: ApiServiceAbstraction, + fileUploadService: FileUploadServiceAbstraction, + i18nService: I18nServiceAbstraction, + injector: Injector, + logService: LogService, + stateService: StateServiceAbstraction + ) => + new CipherService( + cryptoService, + settingsService, + apiService, + fileUploadService, + i18nService, + () => injector.get(SearchServiceAbstraction), + logService, + stateService + ), + deps: [ + CryptoServiceAbstraction, + SettingsServiceAbstraction, + ApiServiceAbstraction, + FileUploadServiceAbstraction, + I18nServiceAbstraction, + Injector, // TODO: Get rid of this circular dependency! + LogService, + StateServiceAbstraction, + ], + }, + { + provide: FolderServiceAbstraction, + useClass: FolderService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + I18nServiceAbstraction, + CipherServiceAbstraction, + StateServiceAbstraction, + ], + }, + { provide: LogService, useFactory: () => new ConsoleLogService(false) }, + { + provide: CollectionServiceAbstraction, + useClass: CollectionService, + deps: [CryptoServiceAbstraction, I18nServiceAbstraction, StateServiceAbstraction], + }, + { + provide: EnvironmentServiceAbstraction, + useClass: EnvironmentService, + deps: [StateServiceAbstraction], + }, + { + provide: TotpServiceAbstraction, + useClass: TotpService, + deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction], + }, + { provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] }, + { + provide: CryptoServiceAbstraction, + useClass: CryptoService, + deps: [ + CryptoFunctionServiceAbstraction, + PlatformUtilsServiceAbstraction, + LogService, + StateServiceAbstraction, + ], + }, + { + provide: PasswordGenerationServiceAbstraction, + useClass: PasswordGenerationService, + deps: [CryptoServiceAbstraction, PolicyServiceAbstraction, StateServiceAbstraction], + }, + { + provide: UsernameGenerationServiceAbstraction, + useClass: UsernameGenerationService, + deps: [CryptoServiceAbstraction, StateServiceAbstraction], + }, + { + provide: ApiServiceAbstraction, + useFactory: ( + tokenService: TokenServiceAbstraction, + platformUtilsService: PlatformUtilsServiceAbstraction, + environmentService: EnvironmentServiceAbstraction, + messagingService: MessagingServiceAbstraction, + appIdService: AppIdServiceAbstraction + ) => + new ApiService( + tokenService, + platformUtilsService, + environmentService, + appIdService, + async (expired: boolean) => messagingService.send("logout", { expired: expired }) + ), + deps: [ + TokenServiceAbstraction, + PlatformUtilsServiceAbstraction, + EnvironmentServiceAbstraction, + MessagingServiceAbstraction, + AppIdServiceAbstraction, + ], + }, + { + provide: FileUploadServiceAbstraction, + useClass: FileUploadService, + deps: [LogService, ApiServiceAbstraction], + }, + { + provide: SyncServiceAbstraction, + useFactory: ( + apiService: ApiServiceAbstraction, + settingsService: SettingsServiceAbstraction, + folderService: FolderServiceAbstraction, + cipherService: CipherServiceAbstraction, + cryptoService: CryptoServiceAbstraction, + collectionService: CollectionServiceAbstraction, + messagingService: MessagingServiceAbstraction, + policyService: PolicyServiceAbstraction, + sendService: SendServiceAbstraction, + logService: LogService, + keyConnectorService: KeyConnectorServiceAbstraction, + stateService: StateServiceAbstraction, + organizationService: OrganizationServiceAbstraction, + providerService: ProviderServiceAbstraction + ) => + new SyncService( + apiService, + settingsService, + folderService, + cipherService, + cryptoService, + collectionService, + messagingService, + policyService, + sendService, + logService, + keyConnectorService, + stateService, + organizationService, + providerService, + async (expired: boolean) => messagingService.send("logout", { expired: expired }) + ), + deps: [ + ApiServiceAbstraction, + SettingsServiceAbstraction, + FolderServiceAbstraction, + CipherServiceAbstraction, + CryptoServiceAbstraction, + CollectionServiceAbstraction, + MessagingServiceAbstraction, + PolicyServiceAbstraction, + SendServiceAbstraction, + LogService, + KeyConnectorServiceAbstraction, + StateServiceAbstraction, + OrganizationServiceAbstraction, + ProviderServiceAbstraction, + ], + }, + { provide: BroadcasterServiceAbstraction, useClass: BroadcasterService }, + { + provide: SettingsServiceAbstraction, + useClass: SettingsService, + deps: [StateServiceAbstraction], + }, + { + provide: VaultTimeoutServiceAbstraction, + useFactory: ( + cipherService: CipherServiceAbstraction, + folderService: FolderServiceAbstraction, + collectionService: CollectionServiceAbstraction, + cryptoService: CryptoServiceAbstraction, + platformUtilsService: PlatformUtilsServiceAbstraction, + messagingService: MessagingServiceAbstraction, + searchService: SearchServiceAbstraction, + tokenService: TokenServiceAbstraction, + policyService: PolicyServiceAbstraction, + keyConnectorService: KeyConnectorServiceAbstraction, + stateService: StateServiceAbstraction + ) => + new VaultTimeoutService( + cipherService, + folderService, + collectionService, + cryptoService, + platformUtilsService, + messagingService, + searchService, + tokenService, + policyService, + keyConnectorService, + stateService, + null, + async (userId?: string) => + messagingService.send("logout", { expired: false, userId: userId }) + ), + deps: [ + CipherServiceAbstraction, + FolderServiceAbstraction, + CollectionServiceAbstraction, + CryptoServiceAbstraction, + PlatformUtilsServiceAbstraction, + MessagingServiceAbstraction, + SearchServiceAbstraction, + TokenServiceAbstraction, + PolicyServiceAbstraction, + KeyConnectorServiceAbstraction, + StateServiceAbstraction, + ], + }, + { + provide: StateServiceAbstraction, + useFactory: ( + storageService: StorageServiceAbstraction, + secureStorageService: StorageServiceAbstraction, + logService: LogService, + stateMigrationService: StateMigrationServiceAbstraction + ) => + new StateService( + storageService, + secureStorageService, + logService, + stateMigrationService, + new StateFactory(GlobalState, Account) + ), + deps: [ + StorageServiceAbstraction, + "SECURE_STORAGE", + LogService, + StateMigrationServiceAbstraction, + ], + }, + { + provide: StateMigrationServiceAbstraction, + useFactory: ( + storageService: StorageServiceAbstraction, + secureStorageService: StorageServiceAbstraction + ) => + new StateMigrationService( + storageService, + secureStorageService, + new StateFactory(GlobalState, Account) + ), + deps: [StorageServiceAbstraction, "SECURE_STORAGE"], + }, + { + provide: ExportServiceAbstraction, + useClass: ExportService, + deps: [ + FolderServiceAbstraction, + CipherServiceAbstraction, + ApiServiceAbstraction, + CryptoServiceAbstraction, + ], + }, + { + provide: SearchServiceAbstraction, + useClass: SearchService, + deps: [CipherServiceAbstraction, LogService, I18nServiceAbstraction], + }, + { + provide: NotificationsServiceAbstraction, + useFactory: ( + syncService: SyncServiceAbstraction, + appIdService: AppIdServiceAbstraction, + apiService: ApiServiceAbstraction, + vaultTimeoutService: VaultTimeoutServiceAbstraction, + environmentService: EnvironmentServiceAbstraction, + messagingService: MessagingServiceAbstraction, + logService: LogService, + stateService: StateServiceAbstraction + ) => + new NotificationsService( + syncService, + appIdService, + apiService, + vaultTimeoutService, + environmentService, + async () => messagingService.send("logout", { expired: true }), + logService, + stateService + ), + deps: [ + SyncServiceAbstraction, + AppIdServiceAbstraction, + ApiServiceAbstraction, + VaultTimeoutServiceAbstraction, + EnvironmentServiceAbstraction, + MessagingServiceAbstraction, + LogService, + StateServiceAbstraction, + ], + }, + { + provide: CryptoFunctionServiceAbstraction, + useClass: WebCryptoFunctionService, + deps: ["WINDOW"], + }, + { + provide: EventServiceAbstraction, + useClass: EventService, + deps: [ + ApiServiceAbstraction, + CipherServiceAbstraction, + StateServiceAbstraction, + LogService, + OrganizationServiceAbstraction, + ], + }, + { + provide: PolicyServiceAbstraction, + useClass: PolicyService, + deps: [StateServiceAbstraction, OrganizationServiceAbstraction, ApiServiceAbstraction], + }, + { + provide: SendServiceAbstraction, + useClass: SendService, + deps: [ + CryptoServiceAbstraction, + ApiServiceAbstraction, + FileUploadServiceAbstraction, + I18nServiceAbstraction, + CryptoFunctionServiceAbstraction, + StateServiceAbstraction, + ], + }, + { + provide: KeyConnectorServiceAbstraction, + useClass: KeyConnectorService, + deps: [ + StateServiceAbstraction, + CryptoServiceAbstraction, + ApiServiceAbstraction, + TokenServiceAbstraction, + LogService, + OrganizationServiceAbstraction, + CryptoFunctionServiceAbstraction, + ], + }, + { + provide: UserVerificationServiceAbstraction, + useClass: UserVerificationService, + deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction], + }, + { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, + { + provide: OrganizationServiceAbstraction, + useClass: OrganizationService, + deps: [StateServiceAbstraction], + }, + { + provide: ProviderServiceAbstraction, + useClass: ProviderService, + deps: [StateServiceAbstraction], + }, + { + provide: TwoFactorServiceAbstraction, + useClass: TwoFactorService, + deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction], + }, + ], +}) +export class JslibServicesModule {} diff --git a/jslib/angular/src/services/lock-guard.service.ts b/jslib/angular/src/services/lock-guard.service.ts new file mode 100644 index 00000000..992c6e5d --- /dev/null +++ b/jslib/angular/src/services/lock-guard.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from "@angular/core"; +import { CanActivate, Router } from "@angular/router"; + +import { StateService } from "jslib-common/abstractions/state.service"; +import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; + +@Injectable() +export class LockGuardService implements CanActivate { + protected homepage = "vault"; + protected loginpage = "login"; + constructor( + private vaultTimeoutService: VaultTimeoutService, + private router: Router, + private stateService: StateService + ) {} + + async canActivate() { + if (await this.vaultTimeoutService.isLocked()) { + return true; + } + + const redirectUrl = (await this.stateService.getIsAuthenticated()) + ? [this.homepage] + : [this.loginpage]; + + this.router.navigate(redirectUrl); + return false; + } +} diff --git a/jslib/angular/src/services/modal.service.ts b/jslib/angular/src/services/modal.service.ts new file mode 100644 index 00000000..aecc84c6 --- /dev/null +++ b/jslib/angular/src/services/modal.service.ts @@ -0,0 +1,180 @@ +import { + ApplicationRef, + ComponentFactory, + ComponentFactoryResolver, + ComponentRef, + EmbeddedViewRef, + Injectable, + Injector, + Type, + ViewContainerRef, +} from "@angular/core"; +import { first } from "rxjs/operators"; + +import { DynamicModalComponent } from "../components/modal/dynamic-modal.component"; +import { ModalInjector } from "../components/modal/modal-injector"; +import { ModalRef } from "../components/modal/modal.ref"; + +export class ModalConfig { + data?: D; + allowMultipleModals = false; +} + +@Injectable() +export class ModalService { + protected modalList: ComponentRef[] = []; + + // Lazy loaded modules are not available in componentFactoryResolver, + // therefore modules needs to manually initialize their resolvers. + private factoryResolvers: Map, ComponentFactoryResolver> = new Map(); + + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private applicationRef: ApplicationRef, + private injector: Injector + ) { + document.addEventListener("keyup", (event) => { + if (event.key === "Escape" && this.modalCount > 0) { + this.topModal.instance.close(); + } + }); + } + + get modalCount() { + return this.modalList.length; + } + + private get topModal() { + return this.modalList[this.modalCount - 1]; + } + + async openViewRef( + componentType: Type, + viewContainerRef: ViewContainerRef, + setComponentParameters: (component: T) => void = null + ): Promise<[ModalRef, T]> { + const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false); + modalComponentRef.instance.setComponentParameters = setComponentParameters; + + viewContainerRef.insert(modalComponentRef.hostView); + + await modalRef.onCreated.pipe(first()).toPromise(); + + return [modalRef, modalComponentRef.instance.componentRef.instance]; + } + + open(componentType: Type, config?: ModalConfig) { + if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) { + return; + } + + // eslint-disable-next-line + const [modalRef, _] = this.openInternal(componentType, config, true); + + return modalRef; + } + + registerComponentFactoryResolver( + componentType: Type, + componentFactoryResolver: ComponentFactoryResolver + ): void { + this.factoryResolvers.set(componentType, componentFactoryResolver); + } + + resolveComponentFactory(componentType: Type): ComponentFactory { + if (this.factoryResolvers.has(componentType)) { + return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType); + } + + return this.componentFactoryResolver.resolveComponentFactory(componentType); + } + + protected openInternal( + componentType: Type, + config?: ModalConfig, + attachToDom?: boolean + ): [ModalRef, ComponentRef] { + const [modalRef, componentRef] = this.createModalComponent(config); + componentRef.instance.childComponentType = componentType; + + if (attachToDom) { + this.applicationRef.attachView(componentRef.hostView); + const domElem = (componentRef.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; + document.body.appendChild(domElem); + } + + modalRef.onClosed.pipe(first()).subscribe(() => { + if (attachToDom) { + this.applicationRef.detachView(componentRef.hostView); + } + componentRef.destroy(); + + this.modalList.pop(); + if (this.modalCount > 0) { + this.topModal.instance.getFocus(); + } + }); + + this.setupHandlers(modalRef); + + this.modalList.push(componentRef); + + return [modalRef, componentRef]; + } + + protected setupHandlers(modalRef: ModalRef) { + let backdrop: HTMLElement = null; + + // Add backdrop, setup [data-dismiss] handler. + modalRef.onCreated.pipe(first()).subscribe((el) => { + document.body.classList.add("modal-open"); + + const modalEl: HTMLElement = el.querySelector(".modal"); + const dialogEl = modalEl.querySelector(".modal-dialog") as HTMLElement; + + backdrop = document.createElement("div"); + backdrop.className = "modal-backdrop fade"; + backdrop.style.zIndex = `${this.modalCount}040`; + modalEl.prepend(backdrop); + + dialogEl.addEventListener("click", (e: Event) => { + e.stopPropagation(); + }); + dialogEl.style.zIndex = `${this.modalCount}050`; + + const modals = Array.from( + el.querySelectorAll('.modal-backdrop, .modal *[data-dismiss="modal"]') + ); + for (const closeElement of modals) { + closeElement.addEventListener("click", () => { + modalRef.close(); + }); + } + }); + + // onClose is used in Web to hook into bootstrap. On other projects we pipe it directly to closed. + modalRef.onClose.pipe(first()).subscribe(() => { + modalRef.closed(); + + if (this.modalCount === 0) { + document.body.classList.remove("modal-open"); + } + }); + } + + protected createModalComponent( + config: ModalConfig + ): [ModalRef, ComponentRef] { + const modalRef = new ModalRef(); + + const map = new WeakMap(); + map.set(ModalConfig, config); + map.set(ModalRef, modalRef); + + const componentFactory = + this.componentFactoryResolver.resolveComponentFactory(DynamicModalComponent); + const componentRef = componentFactory.create(new ModalInjector(this.injector, map)); + + return [modalRef, componentRef]; + } +} diff --git a/jslib/angular/src/services/passwordReprompt.service.ts b/jslib/angular/src/services/passwordReprompt.service.ts new file mode 100644 index 00000000..723c41f7 --- /dev/null +++ b/jslib/angular/src/services/passwordReprompt.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from "@angular/core"; + +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; + +import { PasswordRepromptComponent } from "../components/password-reprompt.component"; + +import { ModalService } from "./modal.service"; + +/** + * Used to verify the user's Master Password for the "Master Password Re-prompt" feature only. + * See UserVerificationService for any other situation where you need to verify the user's identity. + */ +@Injectable() +export class PasswordRepromptService implements PasswordRepromptServiceAbstraction { + protected component = PasswordRepromptComponent; + + constructor( + private modalService: ModalService, + private keyConnectorService: KeyConnectorService + ) {} + + protectedFields() { + return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"]; + } + + async showPasswordPrompt() { + if (!(await this.enabled())) { + return true; + } + + const ref = this.modalService.open(this.component, { allowMultipleModals: true }); + + if (ref == null) { + return false; + } + + const result = await ref.onClosedPromise(); + return result === true; + } + + async enabled() { + return !(await this.keyConnectorService.getUsesKeyConnector()); + } +} diff --git a/jslib/angular/src/services/unauth-guard.service.ts b/jslib/angular/src/services/unauth-guard.service.ts new file mode 100644 index 00000000..ee2da045 --- /dev/null +++ b/jslib/angular/src/services/unauth-guard.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from "@angular/core"; +import { CanActivate, Router } from "@angular/router"; + +import { StateService } from "jslib-common/abstractions/state.service"; +import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; + +@Injectable() +export class UnauthGuardService implements CanActivate { + protected homepage = "vault"; + constructor( + private vaultTimeoutService: VaultTimeoutService, + private router: Router, + private stateService: StateService + ) {} + + async canActivate() { + const isAuthed = await this.stateService.getIsAuthenticated(); + if (isAuthed) { + const locked = await this.vaultTimeoutService.isLocked(); + if (locked) { + this.router.navigate(["lock"]); + } else { + this.router.navigate([this.homepage]); + } + return false; + } + return true; + } +} diff --git a/jslib/angular/src/services/validation.service.ts b/jslib/angular/src/services/validation.service.ts new file mode 100644 index 00000000..865f6458 --- /dev/null +++ b/jslib/angular/src/services/validation.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +@Injectable() +export class ValidationService { + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService + ) {} + + showError(data: any): string[] { + const defaultErrorMessage = this.i18nService.t("unexpectedError"); + let errors: string[] = []; + + if (data != null && typeof data === "string") { + errors.push(data); + } else if (data == null || typeof data !== "object") { + errors.push(defaultErrorMessage); + } else if (data.validationErrors != null) { + errors = errors.concat((data as ErrorResponse).getAllMessages()); + } else { + errors.push(data.message ? data.message : defaultErrorMessage); + } + + if (errors.length === 1) { + this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors[0]); + } else if (errors.length > 1) { + this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors, { + timeout: 5000 * errors.length, + }); + } + + return errors; + } +} diff --git a/jslib/angular/tsconfig.json b/jslib/angular/tsconfig.json new file mode 100644 index 00000000..c2fd662c --- /dev/null +++ b/jslib/angular/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-angular/*": ["./src/"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/angular/tsconfig.spec.json b/jslib/angular/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/angular/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/common/jest.config.js b/jslib/common/jest.config.js new file mode 100644 index 00000000..76856611 --- /dev/null +++ b/jslib/common/jest.config.js @@ -0,0 +1,18 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + name: "common", + displayName: "common jslib tests", + preset: "ts-jest", + testEnvironment: "jsdom", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/common/package-lock.json b/jslib/common/package-lock.json new file mode 100644 index 00000000..2bf7f067 --- /dev/null +++ b/jslib/common/package-lock.json @@ -0,0 +1,956 @@ +{ + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "node_modules/@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "dependencies": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "node_modules/@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "node_modules/@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "node_modules/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 + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "dependencies": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "dependencies": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/papaparse": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", + "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/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==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/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==" + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tough-cookie/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + }, + "dependencies": { + "@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "requires": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "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" + } + }, + "browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "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 + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "requires": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "requires": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "papaparse": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.1.tgz", + "integrity": "sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "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==" + } + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "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" + } + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "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==" + } + } + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + } +} diff --git a/jslib/common/package.json b/jslib/common/package.json new file mode 100644 index 00000000..14df9a75 --- /dev/null +++ b/jslib/common/package.json @@ -0,0 +1,42 @@ +{ + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + } +} diff --git a/jslib/common/spec/domain/attachment.spec.ts b/jslib/common/spec/domain/attachment.spec.ts new file mode 100644 index 00000000..f625adbf --- /dev/null +++ b/jslib/common/spec/domain/attachment.spec.ts @@ -0,0 +1,83 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { AttachmentData } from "jslib-common/models/data/attachmentData"; +import { Attachment } from "jslib-common/models/domain/attachment"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { ContainerService } from "jslib-common/services/container.service"; + +import { makeStaticByteArray, mockEnc } from "../utils"; + +describe("Attachment", () => { + let data: AttachmentData; + + beforeEach(() => { + data = { + id: "id", + url: "url", + fileName: "fileName", + key: "key", + size: "1100", + sizeName: "1.1 KB", + }; + }); + + it("Convert from empty", () => { + const data = new AttachmentData(); + const attachment = new Attachment(data); + + expect(attachment).toEqual({ + id: null, + url: null, + size: undefined, + sizeName: null, + key: null, + fileName: null, + }); + }); + + it("Convert", () => { + const attachment = new Attachment(data); + + expect(attachment).toEqual({ + size: "1100", + id: "id", + url: "url", + sizeName: "1.1 KB", + fileName: { encryptedString: "fileName", encryptionType: 0 }, + key: { encryptedString: "key", encryptionType: 0 }, + }); + }); + + it("toAttachmentData", () => { + const attachment = new Attachment(data); + expect(attachment.toAttachmentData()).toEqual(data); + }); + + it("Decrypt", async () => { + const attachment = new Attachment(); + attachment.id = "id"; + attachment.url = "url"; + attachment.size = "1100"; + attachment.sizeName = "1.1 KB"; + attachment.key = mockEnc("key"); + attachment.fileName = mockEnc("fileName"); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(32)); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const view = await attachment.decrypt(null); + + expect(view).toEqual({ + id: "id", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "fileName", + key: expect.any(SymmetricCryptoKey), + }); + }); +}); diff --git a/jslib/common/spec/domain/card.spec.ts b/jslib/common/spec/domain/card.spec.ts new file mode 100644 index 00000000..f4709bcc --- /dev/null +++ b/jslib/common/spec/domain/card.spec.ts @@ -0,0 +1,73 @@ +import { CardData } from "jslib-common/models/data/cardData"; +import { Card } from "jslib-common/models/domain/card"; + +import { mockEnc } from "../utils"; + +describe("Card", () => { + let data: CardData; + + beforeEach(() => { + data = { + cardholderName: "encHolder", + brand: "encBrand", + number: "encNumber", + expMonth: "encMonth", + expYear: "encYear", + code: "encCode", + }; + }); + + it("Convert from empty", () => { + const data = new CardData(); + const card = new Card(data); + + expect(card).toEqual({ + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }); + }); + + it("Convert", () => { + const card = new Card(data); + + expect(card).toEqual({ + cardholderName: { encryptedString: "encHolder", encryptionType: 0 }, + brand: { encryptedString: "encBrand", encryptionType: 0 }, + number: { encryptedString: "encNumber", encryptionType: 0 }, + expMonth: { encryptedString: "encMonth", encryptionType: 0 }, + expYear: { encryptedString: "encYear", encryptionType: 0 }, + code: { encryptedString: "encCode", encryptionType: 0 }, + }); + }); + + it("toCardData", () => { + const card = new Card(data); + expect(card.toCardData()).toEqual(data); + }); + + it("Decrypt", async () => { + const card = new Card(); + card.cardholderName = mockEnc("cardHolder"); + card.brand = mockEnc("brand"); + card.number = mockEnc("number"); + card.expMonth = mockEnc("expMonth"); + card.expYear = mockEnc("expYear"); + card.code = mockEnc("code"); + + const view = await card.decrypt(null); + + expect(view).toEqual({ + _brand: "brand", + _number: "number", + _subTitle: null, + cardholderName: "cardHolder", + code: "code", + expMonth: "expMonth", + expYear: "expYear", + }); + }); +}); diff --git a/jslib/common/spec/domain/cipher.spec.ts b/jslib/common/spec/domain/cipher.spec.ts new file mode 100644 index 00000000..0e6ed452 --- /dev/null +++ b/jslib/common/spec/domain/cipher.spec.ts @@ -0,0 +1,599 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { CipherData } from "jslib-common/models/data/cipherData"; +import { Card } from "jslib-common/models/domain/card"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { Identity } from "jslib-common/models/domain/identity"; +import { Login } from "jslib-common/models/domain/login"; +import { SecureNote } from "jslib-common/models/domain/secureNote"; +import { CardView } from "jslib-common/models/view/cardView"; +import { IdentityView } from "jslib-common/models/view/identityView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { mockEnc } from "../utils"; + +describe("Cipher DTO", () => { + it("Convert from empty CipherData", () => { + const data = new CipherData(); + const cipher = new Cipher(data); + + expect(cipher).toEqual({ + id: null, + userId: null, + organizationId: null, + folderId: null, + name: null, + notes: null, + type: undefined, + favorite: undefined, + organizationUseTotp: undefined, + edit: undefined, + viewPassword: true, + revisionDate: null, + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: undefined, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + describe("LoginCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Login, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + login: { + uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }], + username: "EncryptedString", + password: "EncryptedString", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "EncryptedString", + autofillOnPageLoad: false, + }, + passwordHistory: [ + { password: "EncryptedString", lastUsedDate: "2022-01-31T12:00:00.000Z" }, + ], + attachments: [ + { + id: "a1", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "file", + key: "EncKey", + }, + { + id: "a2", + url: "url", + size: "1100", + sizeName: "1.1 KB", + fileName: "file", + key: "EncKey", + }, + ], + fields: [ + { + name: "EncryptedString", + value: "EncryptedString", + type: FieldType.Text, + linkedId: null, + }, + { + name: "EncryptedString", + value: "EncryptedString", + type: FieldType.Hidden, + linkedId: null, + }, + ], + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 1, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + login: { + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + autofillOnPageLoad: false, + username: { encryptedString: "EncryptedString", encryptionType: 0 }, + password: { encryptedString: "EncryptedString", encryptionType: 0 }, + totp: { encryptedString: "EncryptedString", encryptionType: 0 }, + uris: [{ match: 0, uri: { encryptedString: "EncryptedString", encryptionType: 0 } }], + }, + attachments: [ + { + fileName: { encryptedString: "file", encryptionType: 0 }, + id: "a1", + key: { encryptedString: "EncKey", encryptionType: 0 }, + size: "1100", + sizeName: "1.1 KB", + url: "url", + }, + { + fileName: { encryptedString: "file", encryptionType: 0 }, + id: "a2", + key: { encryptedString: "EncKey", encryptionType: 0 }, + size: "1100", + sizeName: "1.1 KB", + url: "url", + }, + ], + fields: [ + { + linkedId: null, + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 0, + value: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + { + linkedId: null, + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 1, + value: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + ], + passwordHistory: [ + { + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + password: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + ], + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Login; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const loginView = new LoginView(); + loginView.username = "username"; + loginView.password = "password"; + + const login = Substitute.for(); + login.decrypt(Arg.any(), Arg.any()).resolves(loginView); + cipher.login = login; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 1, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + login: loginView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("SecureNoteCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.SecureNote, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + secureNote: { + type: SecureNoteType.Generic, + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 2, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + secureNote: { type: SecureNoteType.Generic }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.SecureNote; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + cipher.secureNote = new SecureNote(); + cipher.secureNote.type = SecureNoteType.Generic; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 2, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + secureNote: { type: 0 }, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("CardCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Card, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + card: { + cardholderName: "EncryptedString", + brand: "EncryptedString", + number: "EncryptedString", + expMonth: "EncryptedString", + expYear: "EncryptedString", + code: "EncryptedString", + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 3, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + card: { + cardholderName: { encryptedString: "EncryptedString", encryptionType: 0 }, + brand: { encryptedString: "EncryptedString", encryptionType: 0 }, + number: { encryptedString: "EncryptedString", encryptionType: 0 }, + expMonth: { encryptedString: "EncryptedString", encryptionType: 0 }, + expYear: { encryptedString: "EncryptedString", encryptionType: 0 }, + code: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Card; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const cardView = new CardView(); + cardView.cardholderName = "cardholderName"; + cardView.number = "4111111111111111"; + + const card = Substitute.for(); + card.decrypt(Arg.any(), Arg.any()).resolves(cardView); + cipher.card = card; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 3, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + card: cardView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); + + describe("IdentityCipher", () => { + let cipherData: CipherData; + + beforeEach(() => { + cipherData = { + id: "id", + organizationId: "orgId", + folderId: "folderId", + userId: "userId", + edit: true, + viewPassword: true, + organizationUseTotp: true, + favorite: false, + revisionDate: "2022-01-31T12:00:00.000Z", + type: CipherType.Identity, + name: "EncryptedString", + notes: "EncryptedString", + deletedDate: null, + reprompt: CipherRepromptType.None, + identity: { + title: "EncryptedString", + firstName: "EncryptedString", + middleName: "EncryptedString", + lastName: "EncryptedString", + address1: "EncryptedString", + address2: "EncryptedString", + address3: "EncryptedString", + city: "EncryptedString", + state: "EncryptedString", + postalCode: "EncryptedString", + country: "EncryptedString", + company: "EncryptedString", + email: "EncryptedString", + phone: "EncryptedString", + ssn: "EncryptedString", + username: "EncryptedString", + passportNumber: "EncryptedString", + licenseNumber: "EncryptedString", + }, + }; + }); + + it("Convert", () => { + const cipher = new Cipher(cipherData); + + expect(cipher).toEqual({ + id: "id", + userId: "userId", + organizationId: "orgId", + folderId: "folderId", + name: { encryptedString: "EncryptedString", encryptionType: 0 }, + notes: { encryptedString: "EncryptedString", encryptionType: 0 }, + type: 4, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + collectionIds: undefined, + localData: null, + deletedDate: null, + reprompt: 0, + identity: { + title: { encryptedString: "EncryptedString", encryptionType: 0 }, + firstName: { encryptedString: "EncryptedString", encryptionType: 0 }, + middleName: { encryptedString: "EncryptedString", encryptionType: 0 }, + lastName: { encryptedString: "EncryptedString", encryptionType: 0 }, + address1: { encryptedString: "EncryptedString", encryptionType: 0 }, + address2: { encryptedString: "EncryptedString", encryptionType: 0 }, + address3: { encryptedString: "EncryptedString", encryptionType: 0 }, + city: { encryptedString: "EncryptedString", encryptionType: 0 }, + state: { encryptedString: "EncryptedString", encryptionType: 0 }, + postalCode: { encryptedString: "EncryptedString", encryptionType: 0 }, + country: { encryptedString: "EncryptedString", encryptionType: 0 }, + company: { encryptedString: "EncryptedString", encryptionType: 0 }, + email: { encryptedString: "EncryptedString", encryptionType: 0 }, + phone: { encryptedString: "EncryptedString", encryptionType: 0 }, + ssn: { encryptedString: "EncryptedString", encryptionType: 0 }, + username: { encryptedString: "EncryptedString", encryptionType: 0 }, + passportNumber: { encryptedString: "EncryptedString", encryptionType: 0 }, + licenseNumber: { encryptedString: "EncryptedString", encryptionType: 0 }, + }, + attachments: null, + fields: null, + passwordHistory: null, + }); + }); + + it("toCipherData", () => { + const cipher = new Cipher(cipherData); + expect(cipher.toCipherData("userId")).toEqual(cipherData); + }); + + it("Decrypt", async () => { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.organizationId = "orgId"; + cipher.folderId = "folderId"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.organizationUseTotp = true; + cipher.favorite = false; + cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + cipher.type = CipherType.Identity; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.deletedDate = null; + cipher.reprompt = CipherRepromptType.None; + + const identityView = new IdentityView(); + identityView.firstName = "firstName"; + identityView.lastName = "lastName"; + + const identity = Substitute.for(); + identity.decrypt(Arg.any(), Arg.any()).resolves(identityView); + cipher.identity = identity; + + const cipherView = await cipher.decrypt(); + + expect(cipherView).toMatchObject({ + id: "id", + organizationId: "orgId", + folderId: "folderId", + name: "EncryptedString", + notes: "EncryptedString", + type: 4, + favorite: false, + organizationUseTotp: true, + edit: true, + viewPassword: true, + identity: identityView, + attachments: null, + fields: null, + passwordHistory: null, + collectionIds: undefined, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + deletedDate: null, + reprompt: 0, + localData: undefined, + }); + }); + }); +}); diff --git a/jslib/common/spec/domain/collection.spec.ts b/jslib/common/spec/domain/collection.spec.ts new file mode 100644 index 00000000..713bf2eb --- /dev/null +++ b/jslib/common/spec/domain/collection.spec.ts @@ -0,0 +1,66 @@ +import { CollectionData } from "jslib-common/models/data/collectionData"; +import { Collection } from "jslib-common/models/domain/collection"; + +import { mockEnc } from "../utils"; + +describe("Collection", () => { + let data: CollectionData; + + beforeEach(() => { + data = { + id: "id", + organizationId: "orgId", + name: "encName", + externalId: "extId", + readOnly: true, + }; + }); + + it("Convert from empty", () => { + const data = new CollectionData({} as any); + const card = new Collection(data); + + expect(card).toEqual({ + externalId: null, + hidePasswords: null, + id: null, + name: null, + organizationId: null, + readOnly: null, + }); + }); + + it("Convert", () => { + const collection = new Collection(data); + + expect(collection).toEqual({ + id: "id", + organizationId: "orgId", + name: { encryptedString: "encName", encryptionType: 0 }, + externalId: "extId", + readOnly: true, + hidePasswords: null, + }); + }); + + it("Decrypt", async () => { + const collection = new Collection(); + collection.id = "id"; + collection.organizationId = "orgId"; + collection.name = mockEnc("encName"); + collection.externalId = "extId"; + collection.readOnly = false; + collection.hidePasswords = false; + + const view = await collection.decrypt(); + + expect(view).toEqual({ + externalId: "extId", + hidePasswords: false, + id: "id", + name: "encName", + organizationId: "orgId", + readOnly: false, + }); + }); +}); diff --git a/jslib/common/spec/domain/encString.spec.ts b/jslib/common/spec/domain/encString.spec.ts new file mode 100644 index 00000000..e2477447 --- /dev/null +++ b/jslib/common/spec/domain/encString.spec.ts @@ -0,0 +1,195 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { EncryptionType } from "jslib-common/enums/encryptionType"; +import { EncString } from "jslib-common/models/domain/encString"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { ContainerService } from "jslib-common/services/container.service"; + +describe("EncString", () => { + afterEach(() => { + (window as any).bitwardenContainerService = undefined; + }); + + describe("Rsa2048_OaepSha256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "3.data", + encryptionType: 3, + }); + }); + + describe("parse existing", () => { + it("valid", () => { + const encString = new EncString("3.data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "3.data", + encryptionType: 3, + }); + }); + + it("invalid", () => { + const encString = new EncString("3.data|test"); + + expect(encString).toEqual({ + encryptedString: "3.data|test", + encryptionType: 3, + }); + }); + }); + + describe("decrypt", () => { + const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data"); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToUtf8(encString, Arg.any()).resolves("decrypted"); + + beforeEach(() => { + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + }); + + it("decrypts correctly", async () => { + const decrypted = await encString.decrypt(null); + + expect(decrypted).toBe("decrypted"); + }); + + it("result should be cached", async () => { + const decrypted = await encString.decrypt(null); + cryptoService.received(1).decryptToUtf8(Arg.any(), Arg.any()); + + expect(decrypted).toBe("decrypted"); + }); + }); + }); + + describe("AesCbc256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "0.iv|data", + encryptionType: 0, + iv: "iv", + }); + }); + + describe("parse existing", () => { + it("valid", () => { + const encString = new EncString("0.iv|data"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "0.iv|data", + encryptionType: 0, + iv: "iv", + }); + }); + + it("invalid", () => { + const encString = new EncString("0.iv|data|mac"); + + expect(encString).toEqual({ + encryptedString: "0.iv|data|mac", + encryptionType: 0, + }); + }); + }); + }); + + describe("AesCbc256_HmacSha256_B64", () => { + it("constructor", () => { + const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "2.iv|data|mac", + encryptionType: 2, + iv: "iv", + mac: "mac", + }); + }); + + it("valid", () => { + const encString = new EncString("2.iv|data|mac"); + + expect(encString).toEqual({ + data: "data", + encryptedString: "2.iv|data|mac", + encryptionType: 2, + iv: "iv", + mac: "mac", + }); + }); + + it("invalid", () => { + const encString = new EncString("2.iv|data"); + + expect(encString).toEqual({ + encryptedString: "2.iv|data", + encryptionType: 2, + }); + }); + }); + + it("Exit early if null", () => { + const encString = new EncString(null); + + expect(encString).toEqual({ + encryptedString: null, + }); + }); + + describe("decrypt", () => { + it("throws exception when bitwarden container not initialized", async () => { + const encString = new EncString(null); + + expect.assertions(1); + try { + await encString.decrypt(null); + } catch (e) { + expect(e.message).toEqual("global bitwardenContainerService not initialized."); + } + }); + + it("handles value it can't decrypt", async () => { + const encString = new EncString(null); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + cryptoService.decryptToUtf8(encString, Arg.any()).throws("error"); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const decrypted = await encString.decrypt(null); + + expect(decrypted).toBe("[error: cannot decrypt]"); + + expect(encString).toEqual({ + decryptedValue: "[error: cannot decrypt]", + encryptedString: null, + }); + }); + + it("passes along key", async () => { + const encString = new EncString(null); + const key = Substitute.for(); + + const cryptoService = Substitute.for(); + cryptoService.getOrgKey(null).resolves(null); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + await encString.decrypt(null, key); + + cryptoService.received().decryptToUtf8(encString, key); + }); + }); +}); diff --git a/jslib/common/spec/domain/field.spec.ts b/jslib/common/spec/domain/field.spec.ts new file mode 100644 index 00000000..40ceb710 --- /dev/null +++ b/jslib/common/spec/domain/field.spec.ts @@ -0,0 +1,64 @@ +import { FieldType } from "jslib-common/enums/fieldType"; +import { FieldData } from "jslib-common/models/data/fieldData"; +import { Field } from "jslib-common/models/domain/field"; + +import { mockEnc } from "../utils"; + +describe("Field", () => { + let data: FieldData; + + beforeEach(() => { + data = { + type: FieldType.Text, + name: "encName", + value: "encValue", + linkedId: null, + }; + }); + + it("Convert from empty", () => { + const data = new FieldData(); + const field = new Field(data); + + expect(field).toEqual({ + type: undefined, + name: null, + value: null, + linkedId: undefined, + }); + }); + + it("Convert", () => { + const field = new Field(data); + + expect(field).toEqual({ + type: FieldType.Text, + name: { encryptedString: "encName", encryptionType: 0 }, + value: { encryptedString: "encValue", encryptionType: 0 }, + linkedId: null, + }); + }); + + it("toFieldData", () => { + const field = new Field(data); + expect(field.toFieldData()).toEqual(data); + }); + + it("Decrypt", async () => { + const field = new Field(); + field.type = FieldType.Text; + field.name = mockEnc("encName"); + field.value = mockEnc("encValue"); + + const view = await field.decrypt(null); + + expect(view).toEqual({ + type: 0, + name: "encName", + value: "encValue", + newField: false, + showCount: false, + showValue: false, + }); + }); +}); diff --git a/jslib/common/spec/domain/folder.spec.ts b/jslib/common/spec/domain/folder.spec.ts new file mode 100644 index 00000000..64ee40b9 --- /dev/null +++ b/jslib/common/spec/domain/folder.spec.ts @@ -0,0 +1,42 @@ +import { FolderData } from "jslib-common/models/data/folderData"; +import { Folder } from "jslib-common/models/domain/folder"; + +import { mockEnc } from "../utils"; + +describe("Folder", () => { + let data: FolderData; + + beforeEach(() => { + data = { + id: "id", + userId: "userId", + name: "encName", + revisionDate: "2022-01-31T12:00:00.000Z", + }; + }); + + it("Convert", () => { + const field = new Folder(data); + + expect(field).toEqual({ + id: "id", + name: { encryptedString: "encName", encryptionType: 0 }, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); + + it("Decrypt", async () => { + const folder = new Folder(); + folder.id = "id"; + folder.name = mockEnc("encName"); + folder.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + + const view = await folder.decrypt(); + + expect(view).toEqual({ + id: "id", + name: "encName", + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); +}); diff --git a/jslib/common/spec/domain/identity.spec.ts b/jslib/common/spec/domain/identity.spec.ts new file mode 100644 index 00000000..ffaf5049 --- /dev/null +++ b/jslib/common/spec/domain/identity.spec.ts @@ -0,0 +1,134 @@ +import { IdentityData } from "jslib-common/models/data/identityData"; +import { Identity } from "jslib-common/models/domain/identity"; + +import { mockEnc } from "../utils"; + +describe("Identity", () => { + let data: IdentityData; + + beforeEach(() => { + data = { + title: "enctitle", + firstName: "encfirstName", + middleName: "encmiddleName", + lastName: "enclastName", + address1: "encaddress1", + address2: "encaddress2", + address3: "encaddress3", + city: "enccity", + state: "encstate", + postalCode: "encpostalCode", + country: "enccountry", + company: "enccompany", + email: "encemail", + phone: "encphone", + ssn: "encssn", + username: "encusername", + passportNumber: "encpassportNumber", + licenseNumber: "enclicenseNumber", + }; + }); + + it("Convert from empty", () => { + const data = new IdentityData(); + const identity = new Identity(data); + + expect(identity).toEqual({ + address1: null, + address2: null, + address3: null, + city: null, + company: null, + country: null, + email: null, + firstName: null, + lastName: null, + licenseNumber: null, + middleName: null, + passportNumber: null, + phone: null, + postalCode: null, + ssn: null, + state: null, + title: null, + username: null, + }); + }); + + it("Convert", () => { + const identity = new Identity(data); + + expect(identity).toEqual({ + title: { encryptedString: "enctitle", encryptionType: 0 }, + firstName: { encryptedString: "encfirstName", encryptionType: 0 }, + middleName: { encryptedString: "encmiddleName", encryptionType: 0 }, + lastName: { encryptedString: "enclastName", encryptionType: 0 }, + address1: { encryptedString: "encaddress1", encryptionType: 0 }, + address2: { encryptedString: "encaddress2", encryptionType: 0 }, + address3: { encryptedString: "encaddress3", encryptionType: 0 }, + city: { encryptedString: "enccity", encryptionType: 0 }, + state: { encryptedString: "encstate", encryptionType: 0 }, + postalCode: { encryptedString: "encpostalCode", encryptionType: 0 }, + country: { encryptedString: "enccountry", encryptionType: 0 }, + company: { encryptedString: "enccompany", encryptionType: 0 }, + email: { encryptedString: "encemail", encryptionType: 0 }, + phone: { encryptedString: "encphone", encryptionType: 0 }, + ssn: { encryptedString: "encssn", encryptionType: 0 }, + username: { encryptedString: "encusername", encryptionType: 0 }, + passportNumber: { encryptedString: "encpassportNumber", encryptionType: 0 }, + licenseNumber: { encryptedString: "enclicenseNumber", encryptionType: 0 }, + }); + }); + + it("toIdentityData", () => { + const identity = new Identity(data); + expect(identity.toIdentityData()).toEqual(data); + }); + + it("Decrypt", async () => { + const identity = new Identity(); + + identity.title = mockEnc("mockTitle"); + identity.firstName = mockEnc("mockFirstName"); + identity.middleName = mockEnc("mockMiddleName"); + identity.lastName = mockEnc("mockLastName"); + identity.address1 = mockEnc("mockAddress1"); + identity.address2 = mockEnc("mockAddress2"); + identity.address3 = mockEnc("mockAddress3"); + identity.city = mockEnc("mockCity"); + identity.state = mockEnc("mockState"); + identity.postalCode = mockEnc("mockPostalCode"); + identity.country = mockEnc("mockCountry"); + identity.company = mockEnc("mockCompany"); + identity.email = mockEnc("mockEmail"); + identity.phone = mockEnc("mockPhone"); + identity.ssn = mockEnc("mockSsn"); + identity.username = mockEnc("mockUsername"); + identity.passportNumber = mockEnc("mockPassportNumber"); + identity.licenseNumber = mockEnc("mockLicenseNumber"); + + const view = await identity.decrypt(null); + + expect(view).toEqual({ + _firstName: "mockFirstName", + _lastName: "mockLastName", + _subTitle: null, + address1: "mockAddress1", + address2: "mockAddress2", + address3: "mockAddress3", + city: "mockCity", + company: "mockCompany", + country: "mockCountry", + email: "mockEmail", + licenseNumber: "mockLicenseNumber", + middleName: "mockMiddleName", + passportNumber: "mockPassportNumber", + phone: "mockPhone", + postalCode: "mockPostalCode", + ssn: "mockSsn", + state: "mockState", + title: "mockTitle", + username: "mockUsername", + }); + }); +}); diff --git a/jslib/common/spec/domain/login.spec.ts b/jslib/common/spec/domain/login.spec.ts new file mode 100644 index 00000000..88c31a5f --- /dev/null +++ b/jslib/common/spec/domain/login.spec.ts @@ -0,0 +1,101 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { LoginData } from "jslib-common/models/data/loginData"; +import { Login } from "jslib-common/models/domain/login"; +import { LoginUri } from "jslib-common/models/domain/loginUri"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; + +import { mockEnc } from "../utils"; + +describe("Login DTO", () => { + it("Convert from empty LoginData", () => { + const data = new LoginData(); + const login = new Login(data); + + expect(login).toEqual({ + passwordRevisionDate: null, + autofillOnPageLoad: undefined, + username: null, + password: null, + totp: null, + }); + }); + + it("Convert from full LoginData", () => { + const data: LoginData = { + uris: [{ uri: "uri", match: UriMatchType.Domain }], + username: "username", + password: "password", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "123", + autofillOnPageLoad: false, + }; + const login = new Login(data); + + expect(login).toEqual({ + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + autofillOnPageLoad: false, + username: { encryptedString: "username", encryptionType: 0 }, + password: { encryptedString: "password", encryptionType: 0 }, + totp: { encryptedString: "123", encryptionType: 0 }, + uris: [{ match: 0, uri: { encryptedString: "uri", encryptionType: 0 } }], + }); + }); + + it("Initialize without LoginData", () => { + const login = new Login(); + + expect(login).toEqual({}); + }); + + it("Decrypts correctly", async () => { + const loginUri = Substitute.for(); + const loginUriView = new LoginUriView(); + loginUriView.uri = "decrypted uri"; + loginUri.decrypt(Arg.any()).resolves(loginUriView); + + const login = new Login(); + login.uris = [loginUri]; + login.username = mockEnc("encrypted username"); + login.password = mockEnc("encrypted password"); + login.passwordRevisionDate = new Date("2022-01-31T12:00:00.000Z"); + login.totp = mockEnc("encrypted totp"); + login.autofillOnPageLoad = true; + + const loginView = await login.decrypt(null); + expect(loginView).toEqual({ + username: "encrypted username", + password: "encrypted password", + passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), + totp: "encrypted totp", + uris: [ + { + match: null, + _uri: "decrypted uri", + _domain: null, + _hostname: null, + _host: null, + _canLaunch: null, + }, + ], + autofillOnPageLoad: true, + }); + }); + + it("Converts from LoginData and back", () => { + const data: LoginData = { + uris: [{ uri: "uri", match: UriMatchType.Domain }], + username: "username", + password: "password", + passwordRevisionDate: "2022-01-31T12:00:00.000Z", + totp: "123", + autofillOnPageLoad: false, + }; + const login = new Login(data); + + const loginData = login.toLoginData(); + + expect(loginData).toEqual(data); + }); +}); diff --git a/jslib/common/spec/domain/loginUri.spec.ts b/jslib/common/spec/domain/loginUri.spec.ts new file mode 100644 index 00000000..2ea5ab59 --- /dev/null +++ b/jslib/common/spec/domain/loginUri.spec.ts @@ -0,0 +1,57 @@ +import { UriMatchType } from "jslib-common/enums/uriMatchType"; +import { LoginUriData } from "jslib-common/models/data/loginUriData"; +import { LoginUri } from "jslib-common/models/domain/loginUri"; + +import { mockEnc } from "../utils"; + +describe("LoginUri", () => { + let data: LoginUriData; + + beforeEach(() => { + data = { + uri: "encUri", + match: UriMatchType.Domain, + }; + }); + + it("Convert from empty", () => { + const data = new LoginUriData(); + const loginUri = new LoginUri(data); + + expect(loginUri).toEqual({ + match: null, + uri: null, + }); + }); + + it("Convert", () => { + const loginUri = new LoginUri(data); + + expect(loginUri).toEqual({ + match: 0, + uri: { encryptedString: "encUri", encryptionType: 0 }, + }); + }); + + it("toLoginUriData", () => { + const loginUri = new LoginUri(data); + expect(loginUri.toLoginUriData()).toEqual(data); + }); + + it("Decrypt", async () => { + const loginUri = new LoginUri(); + loginUri.match = UriMatchType.Exact; + loginUri.uri = mockEnc("uri"); + + const view = await loginUri.decrypt(null); + + expect(view).toEqual({ + _canLaunch: null, + _domain: null, + _host: null, + _hostname: null, + _uri: "uri", + match: 3, + }); + }); +}); diff --git a/jslib/common/spec/domain/password.spec.ts b/jslib/common/spec/domain/password.spec.ts new file mode 100644 index 00000000..4054c951 --- /dev/null +++ b/jslib/common/spec/domain/password.spec.ts @@ -0,0 +1,51 @@ +import { PasswordHistoryData } from "jslib-common/models/data/passwordHistoryData"; +import { Password } from "jslib-common/models/domain/password"; + +import { mockEnc } from "../utils"; + +describe("Password", () => { + let data: PasswordHistoryData; + + beforeEach(() => { + data = { + password: "encPassword", + lastUsedDate: "2022-01-31T12:00:00.000Z", + }; + }); + + it("Convert from empty", () => { + const data = new PasswordHistoryData(); + const password = new Password(data); + + expect(password).toMatchObject({ + password: null, + }); + }); + + it("Convert", () => { + const password = new Password(data); + + expect(password).toEqual({ + password: { encryptedString: "encPassword", encryptionType: 0 }, + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); + + it("toPasswordHistoryData", () => { + const password = new Password(data); + expect(password.toPasswordHistoryData()).toEqual(data); + }); + + it("Decrypt", async () => { + const password = new Password(); + password.password = mockEnc("password"); + password.lastUsedDate = new Date("2022-01-31T12:00:00.000Z"); + + const view = await password.decrypt(null); + + expect(view).toEqual({ + password: "password", + lastUsedDate: new Date("2022-01-31T12:00:00.000Z"), + }); + }); +}); diff --git a/jslib/common/spec/domain/secureNote.spec.ts b/jslib/common/spec/domain/secureNote.spec.ts new file mode 100644 index 00000000..50ca67ed --- /dev/null +++ b/jslib/common/spec/domain/secureNote.spec.ts @@ -0,0 +1,46 @@ +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { SecureNoteData } from "jslib-common/models/data/secureNoteData"; +import { SecureNote } from "jslib-common/models/domain/secureNote"; + +describe("SecureNote", () => { + let data: SecureNoteData; + + beforeEach(() => { + data = { + type: SecureNoteType.Generic, + }; + }); + + it("Convert from empty", () => { + const data = new SecureNoteData(); + const secureNote = new SecureNote(data); + + expect(secureNote).toEqual({ + type: undefined, + }); + }); + + it("Convert", () => { + const secureNote = new SecureNote(data); + + expect(secureNote).toEqual({ + type: 0, + }); + }); + + it("toSecureNoteData", () => { + const secureNote = new SecureNote(data); + expect(secureNote.toSecureNoteData()).toEqual(data); + }); + + it("Decrypt", async () => { + const secureNote = new SecureNote(); + secureNote.type = SecureNoteType.Generic; + + const view = await secureNote.decrypt(null); + + expect(view).toEqual({ + type: 0, + }); + }); +}); diff --git a/jslib/common/spec/domain/send.spec.ts b/jslib/common/spec/domain/send.spec.ts new file mode 100644 index 00000000..d63c622a --- /dev/null +++ b/jslib/common/spec/domain/send.spec.ts @@ -0,0 +1,144 @@ +import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { SendType } from "jslib-common/enums/sendType"; +import { SendData } from "jslib-common/models/data/sendData"; +import { EncString } from "jslib-common/models/domain/encString"; +import { Send } from "jslib-common/models/domain/send"; +import { SendText } from "jslib-common/models/domain/sendText"; +import { ContainerService } from "jslib-common/services/container.service"; + +import { makeStaticByteArray, mockEnc } from "../utils"; + +describe("Send", () => { + let data: SendData; + + beforeEach(() => { + data = { + id: "id", + accessId: "accessId", + userId: "userId", + type: SendType.Text, + name: "encName", + notes: "encNotes", + text: { + text: "encText", + hidden: true, + }, + file: null, + key: "encKey", + maxAccessCount: null, + accessCount: 10, + revisionDate: "2022-01-31T12:00:00.000Z", + expirationDate: "2022-01-31T12:00:00.000Z", + deletionDate: "2022-01-31T12:00:00.000Z", + password: "password", + disabled: false, + hideEmail: true, + }; + }); + + it("Convert from empty", () => { + const data = new SendData(); + const send = new Send(data); + + expect(send).toEqual({ + id: null, + accessId: null, + userId: null, + type: undefined, + name: null, + notes: null, + text: undefined, + file: undefined, + key: null, + maxAccessCount: undefined, + accessCount: undefined, + revisionDate: null, + expirationDate: null, + deletionDate: null, + password: undefined, + disabled: undefined, + hideEmail: undefined, + }); + }); + + it("Convert", () => { + const send = new Send(data); + + expect(send).toEqual({ + id: "id", + accessId: "accessId", + userId: "userId", + type: SendType.Text, + name: { encryptedString: "encName", encryptionType: 0 }, + notes: { encryptedString: "encNotes", encryptionType: 0 }, + text: { + text: { encryptedString: "encText", encryptionType: 0 }, + hidden: true, + }, + key: { encryptedString: "encKey", encryptionType: 0 }, + maxAccessCount: null, + accessCount: 10, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + deletionDate: new Date("2022-01-31T12:00:00.000Z"), + password: "password", + disabled: false, + hideEmail: true, + }); + }); + + it("Decrypt", async () => { + const text = Substitute.for(); + text.decrypt(Arg.any()).resolves("textView" as any); + + const send = new Send(); + send.id = "id"; + send.accessId = "accessId"; + send.userId = "userId"; + send.type = SendType.Text; + send.name = mockEnc("name"); + send.notes = mockEnc("notes"); + send.text = text; + send.key = mockEnc("key"); + send.accessCount = 10; + send.revisionDate = new Date("2022-01-31T12:00:00.000Z"); + send.expirationDate = new Date("2022-01-31T12:00:00.000Z"); + send.deletionDate = new Date("2022-01-31T12:00:00.000Z"); + send.password = "password"; + send.disabled = false; + send.hideEmail = true; + + const cryptoService = Substitute.for(); + cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32)); + cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService); + + const view = await send.decrypt(); + + text.received(1).decrypt("cryptoKey" as any); + (send.name as SubstituteOf).received(1).decrypt(null, "cryptoKey" as any); + + expect(view).toMatchObject({ + id: "id", + accessId: "accessId", + name: "name", + notes: "notes", + type: 0, + key: expect.anything(), + cryptoKey: "cryptoKey", + file: expect.anything(), + text: "textView", + maxAccessCount: undefined, + accessCount: 10, + revisionDate: new Date("2022-01-31T12:00:00.000Z"), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + deletionDate: new Date("2022-01-31T12:00:00.000Z"), + password: "password", + disabled: false, + hideEmail: true, + }); + }); +}); diff --git a/jslib/common/spec/domain/sendAccess.spec.ts b/jslib/common/spec/domain/sendAccess.spec.ts new file mode 100644 index 00000000..fc3001a5 --- /dev/null +++ b/jslib/common/spec/domain/sendAccess.spec.ts @@ -0,0 +1,84 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { SendType } from "jslib-common/enums/sendType"; +import { SendAccess } from "jslib-common/models/domain/sendAccess"; +import { SendText } from "jslib-common/models/domain/sendText"; +import { SendAccessResponse } from "jslib-common/models/response/sendAccessResponse"; + +import { mockEnc } from "../utils"; + +describe("SendAccess", () => { + let request: SendAccessResponse; + + beforeEach(() => { + request = { + id: "id", + type: SendType.Text, + name: "encName", + file: null, + text: { + text: "encText", + hidden: true, + }, + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + } as SendAccessResponse; + }); + + it("Convert from empty", () => { + const request = new SendAccessResponse({}); + const sendAccess = new SendAccess(request); + + expect(sendAccess).toEqual({ + id: null, + type: undefined, + name: null, + creatorIdentifier: null, + expirationDate: null, + }); + }); + + it("Convert", () => { + const sendAccess = new SendAccess(request); + + expect(sendAccess).toEqual({ + id: "id", + type: 0, + name: { encryptedString: "encName", encryptionType: 0 }, + text: { + hidden: true, + text: { encryptedString: "encText", encryptionType: 0 }, + }, + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + }); + }); + + it("Decrypt", async () => { + const sendAccess = new SendAccess(); + sendAccess.id = "id"; + sendAccess.type = SendType.Text; + sendAccess.name = mockEnc("name"); + + const text = Substitute.for(); + text.decrypt(Arg.any()).resolves({} as any); + sendAccess.text = text; + + sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z"); + sendAccess.creatorIdentifier = "creatorIdentifier"; + + const view = await sendAccess.decrypt(null); + + text.received(1).decrypt(Arg.any()); + + expect(view).toEqual({ + id: "id", + type: 0, + name: "name", + text: {}, + file: expect.anything(), + expirationDate: new Date("2022-01-31T12:00:00.000Z"), + creatorIdentifier: "creatorIdentifier", + }); + }); +}); diff --git a/jslib/common/spec/domain/sendFile.spec.ts b/jslib/common/spec/domain/sendFile.spec.ts new file mode 100644 index 00000000..9fde0b76 --- /dev/null +++ b/jslib/common/spec/domain/sendFile.spec.ts @@ -0,0 +1,57 @@ +import { SendFileData } from "jslib-common/models/data/sendFileData"; +import { SendFile } from "jslib-common/models/domain/sendFile"; + +import { mockEnc } from "../utils"; + +describe("SendFile", () => { + let data: SendFileData; + + beforeEach(() => { + data = { + id: "id", + size: "1100", + sizeName: "1.1 KB", + fileName: "encFileName", + }; + }); + + it("Convert from empty", () => { + const data = new SendFileData(); + const sendFile = new SendFile(data); + + expect(sendFile).toEqual({ + fileName: null, + id: null, + size: undefined, + sizeName: null, + }); + }); + + it("Convert", () => { + const sendFile = new SendFile(data); + + expect(sendFile).toEqual({ + id: "id", + size: "1100", + sizeName: "1.1 KB", + fileName: { encryptedString: "encFileName", encryptionType: 0 }, + }); + }); + + it("Decrypt", async () => { + const sendFile = new SendFile(); + sendFile.id = "id"; + sendFile.size = "1100"; + sendFile.sizeName = "1.1 KB"; + sendFile.fileName = mockEnc("fileName"); + + const view = await sendFile.decrypt(null); + + expect(view).toEqual({ + fileName: "fileName", + id: "id", + size: "1100", + sizeName: "1.1 KB", + }); + }); +}); diff --git a/jslib/common/spec/domain/sendText.spec.ts b/jslib/common/spec/domain/sendText.spec.ts new file mode 100644 index 00000000..776a4c57 --- /dev/null +++ b/jslib/common/spec/domain/sendText.spec.ts @@ -0,0 +1,47 @@ +import { SendTextData } from "jslib-common/models/data/sendTextData"; +import { SendText } from "jslib-common/models/domain/sendText"; + +import { mockEnc } from "../utils"; + +describe("SendText", () => { + let data: SendTextData; + + beforeEach(() => { + data = { + text: "encText", + hidden: false, + }; + }); + + it("Convert from empty", () => { + const data = new SendTextData(); + const secureNote = new SendText(data); + + expect(secureNote).toEqual({ + hidden: undefined, + text: null, + }); + }); + + it("Convert", () => { + const secureNote = new SendText(data); + + expect(secureNote).toEqual({ + hidden: false, + text: { encryptedString: "encText", encryptionType: 0 }, + }); + }); + + it("Decrypt", async () => { + const secureNote = new SendText(); + secureNote.text = mockEnc("text"); + secureNote.hidden = true; + + const view = await secureNote.decrypt(null); + + expect(view).toEqual({ + text: "text", + hidden: true, + }); + }); +}); diff --git a/jslib/common/spec/domain/symmetricCryptoKey.spec.ts b/jslib/common/spec/domain/symmetricCryptoKey.spec.ts new file mode 100644 index 00000000..62f096c1 --- /dev/null +++ b/jslib/common/spec/domain/symmetricCryptoKey.spec.ts @@ -0,0 +1,69 @@ +import { EncryptionType } from "jslib-common/enums/encryptionType"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +import { makeStaticByteArray } from "../utils"; + +describe("SymmetricCryptoKey", () => { + it("errors if no key", () => { + const t = () => { + new SymmetricCryptoKey(null); + }; + + expect(t).toThrowError("Must provide key"); + }); + + describe("guesses encKey from key length", () => { + it("AesCbc256_B64", () => { + const key = makeStaticByteArray(32); + const cryptoKey = new SymmetricCryptoKey(key); + + expect(cryptoKey).toEqual({ + encKey: key, + encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + encType: 0, + key: key, + keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + macKey: null, + }); + }); + + it("AesCbc128_HmacSha256_B64", () => { + const key = makeStaticByteArray(32); + const cryptoKey = new SymmetricCryptoKey(key, EncryptionType.AesCbc128_HmacSha256_B64); + + expect(cryptoKey).toEqual({ + encKey: key.slice(0, 16), + encKeyB64: "AAECAwQFBgcICQoLDA0ODw==", + encType: 1, + key: key, + keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + macKey: key.slice(16, 32), + macKeyB64: "EBESExQVFhcYGRobHB0eHw==", + }); + }); + + it("AesCbc256_HmacSha256_B64", () => { + const key = makeStaticByteArray(64); + const cryptoKey = new SymmetricCryptoKey(key); + + expect(cryptoKey).toEqual({ + encKey: key.slice(0, 32), + encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", + encType: 2, + key: key, + keyB64: + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==", + macKey: key.slice(32, 64), + macKeyB64: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=", + }); + }); + + it("unknown length", () => { + const t = () => { + new SymmetricCryptoKey(makeStaticByteArray(30)); + }; + + expect(t).toThrowError("Unable to determine encType."); + }); + }); +}); diff --git a/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts b/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts new file mode 100644 index 00000000..94b03528 --- /dev/null +++ b/jslib/common/spec/importers/bitwardenJsonImporter.spec.ts @@ -0,0 +1,31 @@ +import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { BitwardenJsonImporter } from "jslib-common/importers/bitwardenJsonImporter"; + +import { data as passwordProtectedData } from "./testData/bitwardenJson/passwordProtected.json"; + +describe("bitwarden json importer", () => { + let sut: BitwardenJsonImporter; + let cryptoService: SubstituteOf; + let i18nService: SubstituteOf; + + beforeEach(() => { + cryptoService = Substitute.for(); + i18nService = Substitute.for(); + + sut = new BitwardenJsonImporter(cryptoService, i18nService); + }); + + it("should fail if password is needed", async () => { + expect((await sut.parse(passwordProtectedData)).success).toBe(false); + }); + + it("should return password needed error message", async () => { + const expected = "Password required error message"; + i18nService.t("importPasswordRequired").returns(expected); + + expect((await sut.parse(passwordProtectedData)).errorMessage).toEqual(expected); + }); +}); diff --git a/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts new file mode 100644 index 00000000..ed2434ee --- /dev/null +++ b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts @@ -0,0 +1,113 @@ +import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { KdfType } from "jslib-common/enums/kdfType"; +import { BitwardenPasswordProtectedImporter } from "jslib-common/importers/bitwardenPasswordProtectedImporter"; +import { Utils } from "jslib-common/misc/utils"; +import { ImportResult } from "jslib-common/models/domain/importResult"; + +import { data as emptyDecryptedData } from "./testData/bitwardenJson/empty.json"; + +describe("BitwardenPasswordProtectedImporter", () => { + let importer: BitwardenPasswordProtectedImporter; + let cryptoService: SubstituteOf; + let i18nService: SubstituteOf; + const password = Utils.newGuid(); + const result = new ImportResult(); + let jDoc: { + encrypted?: boolean; + passwordProtected?: boolean; + salt?: string; + kdfIterations?: any; + kdfType?: any; + encKeyValidation_DO_NOT_EDIT?: string; + data?: string; + }; + + beforeEach(() => { + cryptoService = Substitute.for(); + i18nService = Substitute.for(); + + jDoc = { + encrypted: true, + passwordProtected: true, + salt: "c2FsdA==", + kdfIterations: 100000, + kdfType: KdfType.PBKDF2_SHA256, + encKeyValidation_DO_NOT_EDIT: Utils.newGuid(), + data: Utils.newGuid(), + }; + + result.success = true; + importer = new BitwardenPasswordProtectedImporter(cryptoService, i18nService, password); + }); + + describe("Required Json Data", () => { + it("succeeds with default jdoc", async () => { + cryptoService.decryptToUtf8(Arg.any(), Arg.any()).resolves(emptyDecryptedData); + + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(true); + }); + + it("fails if encrypted === false", async () => { + jDoc.encrypted = false; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if encrypted === null", async () => { + jDoc.encrypted = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if passwordProtected === false", async () => { + jDoc.passwordProtected = false; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if passwordProtected === null", async () => { + jDoc.passwordProtected = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if salt === null", async () => { + jDoc.salt = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfIterations === null", async () => { + jDoc.kdfIterations = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfIterations is not a number", async () => { + jDoc.kdfIterations = "not a number"; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType === null", async () => { + jDoc.kdfType = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType is not a string", async () => { + jDoc.kdfType = "not a valid kdf type"; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if kdfType is not a known kdfType", async () => { + jDoc.kdfType = -1; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if encKeyValidation_DO_NOT_EDIT === null", async () => { + jDoc.encKeyValidation_DO_NOT_EDIT = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + + it("fails if data === null", async () => { + jDoc.data = null; + expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(false); + }); + }); +}); diff --git a/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts b/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts new file mode 100644 index 00000000..666e3232 --- /dev/null +++ b/jslib/common/spec/importers/dashlaneCsvImporter.spec.ts @@ -0,0 +1,367 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { DashlaneCsvImporter as Importer } from "jslib-common/importers/dashlaneImporters/dashlaneCsvImporter"; + +import { credentialsData } from "./testData/dashlaneCsv/credentials.csv"; +import { identityData } from "./testData/dashlaneCsv/id.csv"; +import { multiplePersonalInfoData } from "./testData/dashlaneCsv/multiplePersonalInfo.csv"; +import { paymentsData } from "./testData/dashlaneCsv/payments.csv"; +import { personalInfoData } from "./testData/dashlaneCsv/personalInfo.csv"; +import { secureNoteData } from "./testData/dashlaneCsv/securenotes.csv"; + +describe("Dashlane CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login records", async () => { + const result = await importer.parse(credentialsData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("example.com"); + expect(cipher.login.username).toEqual("jdoe"); + expect(cipher.login.password).toEqual("somePassword"); + expect(cipher.login.totp).toEqual("someTOTPSeed"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://www.example.com"); + expect(cipher.notes).toEqual("some note for example.com"); + }); + + it("should parse an item and create a folder", async () => { + const result = await importer.parse(credentialsData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.folders.length).toBe(1); + expect(result.folders[0].name).toBe("Entertainment"); + expect(result.folderRelationships[0]).toEqual([0, 0]); + }); + + it("should parse payment records", async () => { + const result = await importer.parse(paymentsData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(2); + + // Account + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.name).toBe("John's savings account"); + expect(cipher.card.brand).toBeNull(); + expect(cipher.card.cardholderName).toBe("John Doe"); + expect(cipher.card.number).toBe("accountNumber"); + expect(cipher.card.code).toBeNull(); + expect(cipher.card.expMonth).toBeNull(); + expect(cipher.card.expYear).toBeNull(); + + expect(cipher.fields.length).toBe(4); + + expect(cipher.fields[0].name).toBe("type"); + expect(cipher.fields[0].value).toBe("bank"); + + expect(cipher.fields[1].name).toBe("routing_number"); + expect(cipher.fields[1].value).toBe("routingNumber"); + + expect(cipher.fields[2].name).toBe("country"); + expect(cipher.fields[2].value).toBe("US"); + + expect(cipher.fields[3].name).toBe("issuing_bank"); + expect(cipher.fields[3].value).toBe("US-ALLY"); + + // CreditCard + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.Card); + expect(cipher2.name).toBe("John Doe"); + expect(cipher2.card.brand).toBe("Visa"); + expect(cipher2.card.cardholderName).toBe("John Doe"); + expect(cipher2.card.number).toBe("41111111111111111"); + expect(cipher2.card.code).toBe("123"); + expect(cipher2.card.expMonth).toBe("01"); + expect(cipher2.card.expYear).toBe("23"); + + expect(cipher2.fields.length).toBe(2); + + expect(cipher2.fields[0].name).toBe("type"); + expect(cipher2.fields[0].value).toBe("credit_card"); + + expect(cipher2.fields[1].name).toBe("country"); + expect(cipher2.fields[1].value).toBe("US"); + }); + + it("should parse ids records", async () => { + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + // Type card + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("John Doe card"); + expect(cipher.identity.fullName).toBe("John Doe"); + expect(cipher.identity.firstName).toBe("John"); + expect(cipher.identity.middleName).toBeNull(); + expect(cipher.identity.lastName).toBe("Doe"); + expect(cipher.identity.licenseNumber).toBe("123123123"); + + expect(cipher.fields.length).toBe(3); + + expect(cipher.fields[0].name).toEqual("type"); + expect(cipher.fields[0].value).toEqual("card"); + + expect(cipher.fields[1].name).toEqual("issue_date"); + expect(cipher.fields[1].value).toEqual("2022-1-30"); + + expect(cipher.fields[2].name).toEqual("expiration_date"); + expect(cipher.fields[2].value).toEqual("2032-1-30"); + + // Type passport + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.Identity); + expect(cipher2.name).toBe("John Doe passport"); + expect(cipher2.identity.fullName).toBe("John Doe"); + expect(cipher2.identity.firstName).toBe("John"); + expect(cipher2.identity.middleName).toBeNull(); + expect(cipher2.identity.lastName).toBe("Doe"); + expect(cipher2.identity.passportNumber).toBe("123123123"); + + expect(cipher2.fields.length).toBe(4); + + expect(cipher2.fields[0].name).toEqual("type"); + expect(cipher2.fields[0].value).toEqual("passport"); + expect(cipher2.fields[1].name).toEqual("issue_date"); + expect(cipher2.fields[1].value).toEqual("2022-1-30"); + expect(cipher2.fields[2].name).toEqual("expiration_date"); + expect(cipher2.fields[2].value).toEqual("2032-1-30"); + expect(cipher2.fields[3].name).toEqual("place_of_issue"); + expect(cipher2.fields[3].value).toEqual("somewhere in Germany"); + + // Type license + const cipher3 = result.ciphers.shift(); + expect(cipher3.type).toBe(CipherType.Identity); + expect(cipher3.name).toBe("John Doe license"); + expect(cipher3.identity.fullName).toBe("John Doe"); + expect(cipher3.identity.firstName).toBe("John"); + expect(cipher3.identity.middleName).toBeNull(); + expect(cipher3.identity.lastName).toBe("Doe"); + expect(cipher3.identity.licenseNumber).toBe("1234556"); + expect(cipher3.identity.state).toBe("DC"); + + expect(cipher3.fields.length).toBe(3); + expect(cipher3.fields[0].name).toEqual("type"); + expect(cipher3.fields[0].value).toEqual("license"); + expect(cipher3.fields[1].name).toEqual("issue_date"); + expect(cipher3.fields[1].value).toEqual("2022-8-10"); + expect(cipher3.fields[2].name).toEqual("expiration_date"); + expect(cipher3.fields[2].value).toEqual("2022-10-10"); + + // Type social_security + const cipher4 = result.ciphers.shift(); + expect(cipher4.type).toBe(CipherType.Identity); + expect(cipher4.name).toBe("John Doe social_security"); + expect(cipher4.identity.fullName).toBe("John Doe"); + expect(cipher4.identity.firstName).toBe("John"); + expect(cipher4.identity.middleName).toBeNull(); + expect(cipher4.identity.lastName).toBe("Doe"); + expect(cipher4.identity.ssn).toBe("123123123"); + + expect(cipher4.fields.length).toBe(1); + expect(cipher4.fields[0].name).toEqual("type"); + expect(cipher4.fields[0].value).toEqual("social_security"); + + // Type tax_number + const cipher5 = result.ciphers.shift(); + expect(cipher5.type).toBe(CipherType.Identity); + expect(cipher5.name).toBe("tax_number"); + expect(cipher5.identity.licenseNumber).toBe("123123123"); + + expect(cipher5.fields.length).toBe(1); + expect(cipher5.fields[0].name).toEqual("type"); + expect(cipher5.fields[0].value).toEqual("tax_number"); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("01"); + expect(cipher.notes).toBe("test"); + }); + + it("should parse personal information records (multiple identities)", async () => { + const result = await importer.parse(multiplePersonalInfoData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(6); + + // name + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("MR John Doe"); + + expect(cipher.fields.length).toBe(7); + expect(cipher.fields[0].name).toEqual("type"); + expect(cipher.fields[0].value).toEqual("name"); + expect(cipher.fields[1].name).toEqual("title"); + expect(cipher.fields[1].value).toEqual("MR"); + expect(cipher.fields[2].name).toEqual("first_name"); + expect(cipher.fields[2].value).toEqual("John"); + expect(cipher.fields[3].name).toEqual("last_name"); + expect(cipher.fields[3].value).toEqual("Doe"); + expect(cipher.fields[4].name).toEqual("login"); + expect(cipher.fields[4].value).toEqual("jdoe"); + expect(cipher.fields[5].name).toEqual("date_of_birth"); + expect(cipher.fields[5].value).toEqual("2022-01-30"); + expect(cipher.fields[6].name).toEqual("place_of_birth"); + expect(cipher.fields[6].value).toEqual("world"); + + // email + const cipher2 = result.ciphers.shift(); + expect(cipher2.type).toBe(CipherType.SecureNote); + expect(cipher2.name).toBe("Johns email"); + + expect(cipher2.fields.length).toBe(4); + expect(cipher2.fields[0].name).toEqual("type"); + expect(cipher2.fields[0].value).toEqual("email"); + expect(cipher2.fields[1].name).toEqual("email"); + expect(cipher2.fields[1].value).toEqual("jdoe@example.com"); + expect(cipher2.fields[2].name).toEqual("email_type"); + expect(cipher2.fields[2].value).toEqual("personal"); + expect(cipher2.fields[3].name).toEqual("item_name"); + expect(cipher2.fields[3].value).toEqual("Johns email"); + + // number + const cipher3 = result.ciphers.shift(); + expect(cipher3.type).toBe(CipherType.SecureNote); + expect(cipher3.name).toBe("John's number"); + + expect(cipher3.fields.length).toBe(3); + expect(cipher3.fields[0].name).toEqual("type"); + expect(cipher3.fields[0].value).toEqual("number"); + expect(cipher3.fields[1].name).toEqual("item_name"); + expect(cipher3.fields[1].value).toEqual("John's number"); + expect(cipher3.fields[2].name).toEqual("phone_number"); + expect(cipher3.fields[2].value).toEqual("+49123123123"); + + // address + const cipher4 = result.ciphers.shift(); + expect(cipher4.type).toBe(CipherType.SecureNote); + expect(cipher4.name).toBe("John's home address"); + + expect(cipher4.fields.length).toBe(12); + expect(cipher4.fields[0].name).toEqual("type"); + expect(cipher4.fields[0].value).toEqual("address"); + expect(cipher4.fields[1].name).toEqual("item_name"); + expect(cipher4.fields[1].value).toEqual("John's home address"); + expect(cipher4.fields[2].name).toEqual("address"); + expect(cipher4.fields[2].value).toEqual("1 some street"); + expect(cipher4.fields[3].name).toEqual("country"); + expect(cipher4.fields[3].value).toEqual("de"); + expect(cipher4.fields[4].name).toEqual("state"); + expect(cipher4.fields[4].value).toEqual("DE-0-NW"); + expect(cipher4.fields[5].name).toEqual("city"); + expect(cipher4.fields[5].value).toEqual("some city"); + expect(cipher4.fields[6].name).toEqual("zip"); + expect(cipher4.fields[6].value).toEqual("123123"); + expect(cipher4.fields[7].name).toEqual("address_recipient"); + expect(cipher4.fields[7].value).toEqual("John"); + expect(cipher4.fields[8].name).toEqual("address_building"); + expect(cipher4.fields[8].value).toEqual("1"); + expect(cipher4.fields[9].name).toEqual("address_apartment"); + expect(cipher4.fields[9].value).toEqual("1"); + expect(cipher4.fields[10].name).toEqual("address_floor"); + expect(cipher4.fields[10].value).toEqual("1"); + expect(cipher4.fields[11].name).toEqual("address_door_code"); + expect(cipher4.fields[11].value).toEqual("123"); + + // website + const cipher5 = result.ciphers.shift(); + expect(cipher5.type).toBe(CipherType.SecureNote); + expect(cipher5.name).toBe("Website"); + + expect(cipher5.fields.length).toBe(3); + expect(cipher5.fields[0].name).toEqual("type"); + expect(cipher5.fields[0].value).toEqual("website"); + expect(cipher5.fields[1].name).toEqual("item_name"); + expect(cipher5.fields[1].value).toEqual("Website"); + expect(cipher5.fields[2].name).toEqual("url"); + expect(cipher5.fields[2].value).toEqual("website.com"); + + // 2nd name/identity + const cipher6 = result.ciphers.shift(); + expect(cipher6.type).toBe(CipherType.SecureNote); + expect(cipher6.name).toBe("Mrs Jane Doe"); + + expect(cipher6.fields.length).toBe(7); + expect(cipher6.fields[0].name).toEqual("type"); + expect(cipher6.fields[0].value).toEqual("name"); + expect(cipher6.fields[1].name).toEqual("title"); + expect(cipher6.fields[1].value).toEqual("Mrs"); + expect(cipher6.fields[2].name).toEqual("first_name"); + expect(cipher6.fields[2].value).toEqual("Jane"); + expect(cipher6.fields[3].name).toEqual("last_name"); + expect(cipher6.fields[3].value).toEqual("Doe"); + expect(cipher6.fields[4].name).toEqual("login"); + expect(cipher6.fields[4].value).toEqual("jdoe"); + expect(cipher6.fields[5].name).toEqual("date_of_birth"); + expect(cipher6.fields[5].value).toEqual("2022-01-30"); + expect(cipher6.fields[6].name).toEqual("place_of_birth"); + expect(cipher6.fields[6].value).toEqual("earth"); + }); + + it("should combine personal information records to one identity if only one identity present", async () => { + const result = await importer.parse(personalInfoData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("MR John Doe"); + expect(cipher.identity.fullName).toBe("MR John Doe"); + expect(cipher.identity.title).toBe("MR"); + expect(cipher.identity.firstName).toBe("John"); + expect(cipher.identity.middleName).toBeNull(); + expect(cipher.identity.lastName).toBe("Doe"); + expect(cipher.identity.username).toBe("jdoe"); + expect(cipher.identity.email).toBe("jdoe@example.com"); + expect(cipher.identity.phone).toBe("+49123123123"); + + expect(cipher.fields.length).toBe(9); + expect(cipher.fields[0].name).toBe("date_of_birth"); + expect(cipher.fields[0].value).toBe("2022-01-30"); + + expect(cipher.fields[1].name).toBe("place_of_birth"); + expect(cipher.fields[1].value).toBe("world"); + + expect(cipher.fields[2].name).toBe("email_type"); + expect(cipher.fields[2].value).toBe("personal"); + + expect(cipher.fields[3].name).toBe("address_recipient"); + expect(cipher.fields[3].value).toBe("John"); + + expect(cipher.fields[4].name).toBe("address_building"); + expect(cipher.fields[4].value).toBe("1"); + + expect(cipher.fields[5].name).toBe("address_apartment"); + expect(cipher.fields[5].value).toBe("1"); + + expect(cipher.fields[6].name).toBe("address_floor"); + expect(cipher.fields[6].value).toBe("1"); + + expect(cipher.fields[7].name).toBe("address_door_code"); + expect(cipher.fields[7].value).toBe("123"); + + expect(cipher.fields[8].name).toBe("url"); + expect(cipher.fields[8].value).toBe("website.com"); + }); +}); diff --git a/jslib/common/spec/importers/firefoxCsvImporter.spec.ts b/jslib/common/spec/importers/firefoxCsvImporter.spec.ts new file mode 100644 index 00000000..e15b86e9 --- /dev/null +++ b/jslib/common/spec/importers/firefoxCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { FirefoxCsvImporter as Importer } from "jslib-common/importers/firefoxCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { data as firefoxAccountsData } from "./testData/firefoxCsv/firefoxAccountsData.csv"; +import { data as simplePasswordData } from "./testData/firefoxCsv/simplePasswordData.csv"; + +const CipherData = [ + { + title: "should parse password", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com", + login: Object.assign(new LoginView(), { + username: "foo", + password: "bar", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, + { + title: 'should skip "chrome://FirefoxAccounts"', + csv: firefoxAccountsData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com", + login: Object.assign(new LoginView(), { + username: "foo", + password: "bar", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, +]; + +describe("Firefox CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/jslib/common/spec/importers/fsecureFskImporter.spec.ts b/jslib/common/spec/importers/fsecureFskImporter.spec.ts new file mode 100644 index 00000000..63d082df --- /dev/null +++ b/jslib/common/spec/importers/fsecureFskImporter.spec.ts @@ -0,0 +1,77 @@ +import { FSecureFskImporter as Importer } from "jslib-common/importers/fsecureFskImporter"; + +const TestDataWithStyleSetToWebsite: string = JSON.stringify({ + data: { + "8d58b5cf252dd06fbd98f5289e918ab1": { + color: "#00baff", + reatedDate: 1609302913, + creditCvv: "", + creditExpiry: "", + creditNumber: "", + favorite: 0, + modifiedDate: 1609302913, + notes: "note", + password: "word", + passwordList: [], + passwordModifiedDate: 1609302913, + rev: 1, + service: "My first pass", + style: "website", + type: 1, + url: "https://bitwarden.com", + username: "pass", + }, + }, +}); + +const TestDataWithStyleSetToGlobe: string = JSON.stringify({ + data: { + "8d58b5cf252dd06fbd98f5289e918ab1": { + color: "#00baff", + reatedDate: 1609302913, + creditCvv: "", + creditExpiry: "", + creditNumber: "", + favorite: 0, + modifiedDate: 1609302913, + notes: "note", + password: "word", + passwordList: [], + passwordModifiedDate: 1609302913, + rev: 1, + service: "My first pass", + style: "globe", + type: 1, + url: "https://bitwarden.com", + username: "pass", + }, + }, +}); + +describe("FSecure FSK Importer", () => { + it("should parse data with style set to website", async () => { + const importer = new Importer(); + const result = await importer.parse(TestDataWithStyleSetToWebsite); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("pass"); + expect(cipher.login.password).toEqual("word"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://bitwarden.com"); + }); + + it("should parse data with style set to globe", async () => { + const importer = new Importer(); + const result = await importer.parse(TestDataWithStyleSetToGlobe); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("pass"); + expect(cipher.login.password).toEqual("word"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://bitwarden.com"); + }); +}); diff --git a/jslib/common/spec/importers/keepass2XmlImporter.spec.ts b/jslib/common/spec/importers/keepass2XmlImporter.spec.ts new file mode 100644 index 00000000..6b539468 --- /dev/null +++ b/jslib/common/spec/importers/keepass2XmlImporter.spec.ts @@ -0,0 +1,189 @@ +import { KeePass2XmlImporter as Importer } from "jslib-common/importers/keepass2XmlImporter"; + +const TestData = ` + + + KeePass + + 2016-12-31T21:33:52Z + + 2016-12-31T21:33:52Z + + 2016-12-31T21:33:52Z + 365 + + 2016-12-31T21:33:59Z + -1 + -1 + + False + False + True + False + False + + True + AAAAAAAAAAAAAAAAAAAAAA== + 2016-12-31T21:33:52Z + AAAAAAAAAAAAAAAAAAAAAA== + 2016-12-31T21:33:52Z + 10 + 6291456 + AAAAAAAAAAAAAAAAAAAAAA== + AAAAAAAAAAAAAAAAAAAAAA== + + + + + + KvS57lVwl13AfGFLwkvq4Q== + Root + + 48 + + 2016-12-31T21:33:52Z + 2016-12-31T21:33:52Z + 2017-01-01T22:58:00Z + 2016-12-31T21:33:52Z + False + 1 + 2016-12-31T21:33:52Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + P0ParXgGMBW6caOL2YrhqQ== + Folder2 + a note about the folder + 48 + + 2016-12-31T21:43:30Z + 2016-12-31T21:43:43Z + 2017-01-01T22:58:00Z + 2016-12-31T21:43:30Z + False + 1 + 2016-12-31T21:43:43Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + fAa543oYlgnJKkhKag5HLw== + 1 + + + + + + 2016-12-31T21:34:13Z + 2016-12-31T21:40:23Z + 2016-12-31T21:40:23Z + 2016-12-31T21:34:13Z + False + 0 + 2016-12-31T21:43:48Z + + + att2 + att2value + + + attr1 + att1value + +line1 +line2 + + + Notes + This is a note!!! + +line1 +line2 + + + Password + googpass + + + Title + Google + + + URL + google.com + + + UserName + googleuser + + + True + 0 + + + + fAa543oYlgnJKkhKag5HLw== + 0 + + + + + + 2016-12-31T21:34:13Z + 2016-12-31T21:34:40Z + 2016-12-31T21:34:40Z + 2016-12-31T21:34:13Z + False + 0 + 2016-12-31T21:34:40Z + + + Notes + This is a note!!! + +line1 +line2 + + + Password + googpass + + + Title + Google + + + URL + google.com + + + UserName + googleuser + + + True + 0 + + + + + + + + +`; + +describe("KeePass2 Xml Importer", () => { + it("should parse XML data", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + }); +}); diff --git a/jslib/common/spec/importers/keeperJsonImporter.spec.ts b/jslib/common/spec/importers/keeperJsonImporter.spec.ts new file mode 100644 index 00000000..3da6ac48 --- /dev/null +++ b/jslib/common/spec/importers/keeperJsonImporter.spec.ts @@ -0,0 +1,108 @@ +import { KeeperJsonImporter as Importer } from "jslib-common/importers/keeperImporters/keeperJsonImporter"; +import { Utils } from "jslib-common/misc/utils"; + +import { testData as TestData } from "./testData/keeperJson/testData"; + +describe("Keeper Json Importer", () => { + const testDataJson = JSON.stringify(TestData); + + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login data", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Bank Account 1"); + expect(cipher.login.username).toEqual("customer1234"); + expect(cipher.login.password).toEqual("4813fJDHF4239fdk"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://chase.com"); + expect(cipher.notes).toEqual("These are some notes."); + + const cipher2 = result.ciphers.shift(); + expect(cipher2.name).toEqual("Bank Account 2"); + expect(cipher2.login.username).toEqual("mybankusername"); + expect(cipher2.login.password).toEqual("w4k4k193f$^&@#*%2"); + expect(cipher2.login.uris.length).toEqual(1); + const uriView2 = cipher2.login.uris.shift(); + expect(uriView2.uri).toEqual("https://amex.com"); + expect(cipher2.notes).toEqual("Some great information here."); + + const cipher3 = result.ciphers.shift(); + expect(cipher3.name).toEqual("Some Account"); + expect(cipher3.login.username).toEqual("someUserName"); + expect(cipher3.login.password).toEqual("w4k4k1wergf$^&@#*%2"); + expect(cipher3.notes).toBeNull(); + expect(cipher3.fields).toBeNull(); + expect(cipher3.login.uris.length).toEqual(1); + const uriView3 = cipher3.login.uris.shift(); + expect(uriView3.uri).toEqual("https://example.com"); + }); + + it("should import TOTP when present", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.totp).toBeNull(); + + // 2nd Cipher + const cipher2 = result.ciphers.shift(); + expect(cipher2.login.totp).toEqual( + "otpauth://totp/Amazon:me@company.com?secret=JBSWY3DPEHPK3PXP&issuer=Amazon&algorithm=SHA1&digits=6&period=30" + ); + }); + + it("should parse custom fields", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.fields.length).toBe(1); + expect(cipher.fields[0].name).toEqual("Account Number"); + expect(cipher.fields[0].value).toEqual("123-456-789"); + + // 2nd Cipher + const cipher2 = result.ciphers.shift(); + expect(cipher2.fields.length).toBe(2); + expect(cipher2.fields[0].name).toEqual("Security Group"); + expect(cipher2.fields[0].value).toEqual("Public"); + + expect(cipher2.fields[1].name).toEqual("IP Address"); + expect(cipher2.fields[1].value).toEqual("12.45.67.8"); + }); + + it("should create folders and assigned ciphers to them", async () => { + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(2); + expect(folders[0].name).toBe("Optional Private Folder 1"); + expect(folders[1].name).toBe("My Customer 1"); + + expect(result.folderRelationships[0]).toEqual([0, 0]); + expect(result.folderRelationships[1]).toEqual([1, 0]); + expect(result.folderRelationships[2]).toEqual([1, 1]); + }); + + it("should create collections if part of an organization", async () => { + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(2); + expect(collections[0].name).toBe("Optional Private Folder 1"); + expect(collections[1].name).toBe("My Customer 1"); + + expect(result.collectionRelationships[0]).toEqual([0, 0]); + expect(result.collectionRelationships[1]).toEqual([1, 0]); + expect(result.collectionRelationships[2]).toEqual([1, 1]); + }); +}); diff --git a/jslib/common/spec/importers/lastpassCsvImporter.spec.ts b/jslib/common/spec/importers/lastpassCsvImporter.spec.ts new file mode 100644 index 00000000..9cca6c4f --- /dev/null +++ b/jslib/common/spec/importers/lastpassCsvImporter.spec.ts @@ -0,0 +1,202 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { LastPassCsvImporter as Importer } from "jslib-common/importers/lastpassCsvImporter"; +import { ImportResult } from "jslib-common/models/domain/importResult"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +function baseExcept(result: ImportResult) { + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); +} + +function expectLogin(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Login); + + expect(cipher.name).toBe("example.com"); + expect(cipher.notes).toBe("super secure notes"); + expect(cipher.login.uri).toBe("http://example.com"); + expect(cipher.login.username).toBe("someUser"); + expect(cipher.login.password).toBe("myPassword"); + expect(cipher.login.totp).toBe("Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G"); +} + +const CipherData = [ + { + title: "should parse expiration date", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type: +Number:1234567812345678 +Security Code:123 +Start Date:October,2017 +Expiration Date:June,2020 +Notes:some text +",Credit-card,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "Credit-card", + notes: "some text\n", + type: 3, + card: { + cardholderName: "John Doe", + number: "1234567812345678", + code: "123", + expYear: "2020", + expMonth: "6", + }, + fields: [ + Object.assign(new FieldView(), { + name: "Start Date", + value: "October,2017", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse blank card note", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card: +Type: +Number: +Security Code: +Start Date:, +Expiration Date:, +Notes:",empty,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "empty", + notes: null, + type: 3, + card: { + expMonth: undefined, + }, + fields: [ + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse card expiration date w/ no exp year", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type:Visa +Number:1234567887654321 +Security Code:321 +Start Date:, +Expiration Date:January, +Notes:",noyear,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "noyear", + notes: null, + type: 3, + card: { + cardholderName: "John Doe", + number: "1234567887654321", + code: "321", + expMonth: "1", + }, + fields: [ + Object.assign(new FieldView(), { + name: "Type", + value: "Visa", + type: FieldType.Text, + }), + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, + { + title: "should parse card expiration date w/ no month", + csv: `url,username,password,extra,name,grouping,fav +http://sn,,,"NoteType:Credit Card +Name on Card:John Doe +Type:Mastercard +Number:8765432112345678 +Security Code:987 +Start Date:, +Expiration Date:,2020 +Notes:",nomonth,,0`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "nomonth", + notes: null, + type: 3, + card: { + cardholderName: "John Doe", + number: "8765432112345678", + code: "987", + expYear: "2020", + expMonth: undefined, + }, + fields: [ + Object.assign(new FieldView(), { + name: "Type", + value: "Mastercard", + type: FieldType.Text, + }), + Object.assign(new FieldView(), { + name: "Start Date", + value: ",", + type: FieldType.Text, + }), + ], + }), + }, +]; + +describe("Lastpass CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); + + it("should parse login with totp", async () => { + const input = `url,username,password,totp,extra,name,grouping,fav + http://example.com,someUser,myPassword,Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G,super secure notes,example.com,,0`; + + const importer = new Importer(); + const result = await importer.parse(input); + baseExcept(result); + + const cipher = result.ciphers[0]; + expectLogin(cipher); + }); +}); diff --git a/jslib/common/spec/importers/mykiCsvImporter.spec.ts b/jslib/common/spec/importers/mykiCsvImporter.spec.ts new file mode 100644 index 00000000..d9538ee7 --- /dev/null +++ b/jslib/common/spec/importers/mykiCsvImporter.spec.ts @@ -0,0 +1,633 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { MykiCsvImporter as Importer } from "jslib-common/importers/mykiCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +import { userAccountData } from "./testData/mykiCsv/UserAccount.csv"; +import { userCreditCardData } from "./testData/mykiCsv/UserCreditCard.csv"; +import { userIdCardData } from "./testData/mykiCsv/UserIdCard.csv"; +import { userIdentityData } from "./testData/mykiCsv/UserIdentity.csv"; +import { userNoteData } from "./testData/mykiCsv/UserNote.csv"; +import { userTwoFaData } from "./testData/mykiCsv/UserTwofa.csv"; + +function expectDriversLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Joe User's nickname"); + expect(cipher.notes).toBe("Additional information"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Driver's License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("02/02/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("02/02/2024"); +} + +function expectPassport(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Passport ID card"); + expect(cipher.notes).toBe("Additional information field"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.passportNumber).toBe("1234567"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Passport"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectSocialSecurity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Social Security ID card"); + expect(cipher.notes).toBe("Additional information field text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.ssn).toBe("123455678"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Social Security"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectIdCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("ID card type ID card"); + expect(cipher.notes).toBe("Additional Information field text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234566"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("ID Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectTaxNumber(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Tax number ID card"); + expect(cipher.notes).toBe("Additinoal information text field"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345678"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Tax Number"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectBankAccount(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Bank account ID card"); + expect(cipher.notes).toBe("Additional text information here"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12344556677"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Bank Account"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectInsuranceCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Insurance card ID card"); + expect(cipher.notes).toBe("Additional information text goes here"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456677"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Insurance Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2022"); +} + +function expectHealthCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Health card Id card"); + expect(cipher.notes).toBe("More info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234670"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Health Card"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectMembershipCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Membership ID card"); + expect(cipher.notes).toBe("Add'l info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345709"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Membership"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectDatabase(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Database ID card"); + expect(cipher.notes).toBe("Addin't info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345089u"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Database"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectOutdoorLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Outdoor license ID card"); + expect(cipher.notes).toBe("Additional info"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123890090"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Outdoor License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectRewardProgram(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Reward program Id card"); + expect(cipher.notes).toBe("1234890"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("12345890b"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Reward Program"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectSoftwareLicense(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Software license ID card"); + expect(cipher.notes).toBe( + "It seems like the fields don't change, which makes it pretty useless that they have so many ID card types." + ); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("1234567c"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Software License"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +function expectTourVisa(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Tour visa ID card"); + expect(cipher.notes).toBe("Additional Informaion text"); + + expect(cipher.identity.fullName).toBe("Joe M User"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.licenseNumber).toBe("123456lkhj"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(5); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toEqual("tags"); + expect(cipher.fields[1].value).toEqual("someTag"); + + expect(cipher.fields[2].name).toEqual("idType"); + expect(cipher.fields[2].value).toEqual("Tour Visa"); + + expect(cipher.fields[3].name).toEqual("idIssuanceDate"); + expect(cipher.fields[3].value).toEqual("03/07/2022"); + + expect(cipher.fields[4].name).toEqual("idExpirationDate"); + expect(cipher.fields[4].value).toEqual("03/07/2028"); +} + +describe("Myki CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse userAccount records", async () => { + const result = await importer.parse(userAccountData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("PasswordNickname"); + expect(cipher.login.username).toEqual("user.name@email.com"); + expect(cipher.login.password).toEqual("abc123"); + expect(cipher.login.totp).toEqual("someTOTPSeed"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("http://www.google.com"); + expect(cipher.notes).toEqual("This is the additional information text."); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse userTwoFa records", async () => { + const result = await importer.parse(userTwoFaData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("2FA nickname"); + expect(cipher.login.username).toBeNull(); + expect(cipher.login.password).toBeNull(); + expect(cipher.login.totp).toBe("someTOTPSeed"); + expect(cipher.notes).toEqual("Additional information field content."); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse creditCard records", async () => { + const result = await importer.parse(userCreditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.name).toBe("Visa test card"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.cardholderName).toBe("Joe User"); + expect(cipher.card.number).toBe("4111111111111111"); + expect(cipher.card.code).toBe("222"); + expect(cipher.card.expMonth).toBe("04"); + expect(cipher.card.expYear).toBe("24"); + + expect(cipher.notes).toBe("This is the additional information field"); + + expect(cipher.fields.length).toBe(2); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + }); + + it("should parse identity records", async () => { + const result = await importer.parse(userIdentityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Identity); + expect(cipher.name).toBe("Joe User's nickname"); + expect(cipher.identity.fullName).toBe("Mr Joe M User"); + expect(cipher.identity.title).toBe("Mr"); + expect(cipher.identity.firstName).toBe("Joe"); + expect(cipher.identity.middleName).toBe("M"); + expect(cipher.identity.lastName).toBe("User"); + expect(cipher.identity.email).toBe("joe.user@email.com"); + + expect(cipher.identity.address1).toBe("1 Example House"); + expect(cipher.identity.address2).toBe("Suite 300"); + + expect(cipher.identity.city).toBe("Portland"); + expect(cipher.identity.postalCode).toBe("04101"); + expect(cipher.identity.country).toBe("United States"); + + expect(cipher.fields.length).toBe(4); + + expect(cipher.fields[0].name).toEqual("status"); + expect(cipher.fields[0].value).toEqual("active"); + + expect(cipher.fields[1].name).toBe("tags"); + expect(cipher.fields[1].value).toBe("someTag"); + + expect(cipher.fields[2].name).toEqual("gender"); + expect(cipher.fields[2].value).toEqual("Male"); + + expect(cipher.fields[3].name).toEqual("number"); + expect(cipher.fields[3].value).toEqual("2223334444"); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(userNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.SecureNote); + expect(cipher.name).toBe("The title of a secure note"); + expect(cipher.notes).toBe("The content of a secure note. Lorem ipsum, etc."); + + expect(cipher.fields.length).toBe(1); + + expect(cipher.fields[0].name).toBe("status"); + expect(cipher.fields[0].value).toBe("active"); + }); + + it("should parse idCard records", async () => { + const result = await importer.parse(userIdCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + + expect(result.ciphers.length).toBe(14); + + // Driver's license + const cipher = result.ciphers.shift(); + expectDriversLicense(cipher); + + // Passport + const cipher2 = result.ciphers.shift(); + expectPassport(cipher2); + + // Social Security + const cipher3 = result.ciphers.shift(); + expectSocialSecurity(cipher3); + + // Id Card + const cipher4 = result.ciphers.shift(); + expectIdCard(cipher4); + + // Tax Number + const cipher5 = result.ciphers.shift(); + expectTaxNumber(cipher5); + + // Bank Account + const cipher6 = result.ciphers.shift(); + expectBankAccount(cipher6); + + // Insurance card + const cipher7 = result.ciphers.shift(); + expectInsuranceCard(cipher7); + + // Health card + const cipher8 = result.ciphers.shift(); + expectHealthCard(cipher8); + + // Membership card + const cipher9 = result.ciphers.shift(); + expectMembershipCard(cipher9); + + // Database card + const cipher10 = result.ciphers.shift(); + expectDatabase(cipher10); + + // Outdoor license + const cipher11 = result.ciphers.shift(); + expectOutdoorLicense(cipher11); + + // Reward program + const cipher12 = result.ciphers.shift(); + expectRewardProgram(cipher12); + + // Software license + const cipher13 = result.ciphers.shift(); + expectSoftwareLicense(cipher13); + + // Tour visa + const cipher14 = result.ciphers.shift(); + expectTourVisa(cipher14); + }); +}); diff --git a/jslib/common/spec/importers/nordpassCsvImporter.spec.ts b/jslib/common/spec/importers/nordpassCsvImporter.spec.ts new file mode 100644 index 00000000..4022b7e3 --- /dev/null +++ b/jslib/common/spec/importers/nordpassCsvImporter.spec.ts @@ -0,0 +1,181 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { NordPassCsvImporter as Importer } from "jslib-common/importers/nordpassCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { IdentityView } from "jslib-common/models/view/identityView"; + +import { data as creditCardData } from "./testData/nordpassCsv/nordpass.card.csv"; +import { data as identityData } from "./testData/nordpassCsv/nordpass.identity.csv"; +import { data as loginData } from "./testData/nordpassCsv/nordpass.login.csv"; +import { data as secureNoteData } from "./testData/nordpassCsv/nordpass.secureNote.csv"; + +const namesTestData = [ + { + title: "Given #fullName should set firstName", + fullName: "MyFirstName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: null, + lastName: null, + }), + }, + { + title: "Given #fullName should set first- and lastName", + fullName: "MyFirstName MyLastName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: null, + lastName: "MyLastName", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName", + fullName: "MyFirstName MyMiddleName MyLastName", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName with Jr", + fullName: "MyFirstName MyMiddleName MyLastName Jr", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName Jr", + }), + }, + { + title: "Given #fullName should set first-, middle and lastName with Jr and III", + fullName: "MyFirstName MyMiddleName MyLastName Jr III", + expected: Object.assign(new IdentityView(), { + firstName: "MyFirstName", + middleName: "MyMiddleName", + lastName: "MyLastName Jr III", + }), + }, +]; + +function expectLogin(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Login); + + expect(cipher.name).toBe("SomeVaultItemName"); + expect(cipher.notes).toBe("Some note for the VaultItem"); + expect(cipher.login.uri).toBe("https://example.com"); + expect(cipher.login.username).toBe("hello@bitwarden.com"); + expect(cipher.login.password).toBe("someStrongPassword"); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.name).toBe("SomeVisa"); + expect(cipher.card.brand).toBe("Visa"); + expect(cipher.card.cardholderName).toBe("SomeHolder"); + expect(cipher.card.number).toBe("4024007103939509"); + expect(cipher.card.code).toBe("123"); + expect(cipher.card.expMonth).toBe("1"); + expect(cipher.card.expYear).toBe("22"); +} + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.name).toBe("SomeTitle"); + expect(cipher.identity.fullName).toBe("MyFirstName MyMiddleName MyLastName"); + expect(cipher.identity.firstName).toBe("MyFirstName"); + expect(cipher.identity.middleName).toBe("MyMiddleName"); + expect(cipher.identity.lastName).toBe("MyLastName"); + expect(cipher.identity.email).toBe("hello@bitwarden.com"); + expect(cipher.identity.phone).toBe("123456789"); + + expect(cipher.identity.address1).toBe("Test street 123"); + expect(cipher.identity.address2).toBe("additional addressinfo"); + expect(cipher.identity.postalCode).toBe("123456"); + expect(cipher.identity.city).toBe("Cologne"); + expect(cipher.identity.state).toBe("North-Rhine-Westphalia"); + expect(cipher.identity.country).toBe("GERMANY"); + expect(cipher.notes).toBe("SomeNoteToMyIdentity"); +} + +function expectSecureNote(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.SecureNote); + + expect(cipher.name).toBe("MySuperSecureNoteTitle"); + expect(cipher.secureNote.type).toBe(SecureNoteType.Generic); + expect(cipher.notes).toBe("MySuperSecureNote"); +} + +describe("NordPass CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse login records", async () => { + const result = await importer.parse(loginData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectLogin(cipher); + }); + + it("should parse credit card records", async () => { + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse identity records", async () => { + const result = await importer.parse( + identityData.replace("#fullName", "MyFirstName MyMiddleName MyLastName") + ); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + namesTestData.forEach((data) => { + it(data.title.replace("#fullName", data.fullName), async () => { + const result = await importer.parse(identityData.replace("#fullName", data.fullName)); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.identity.firstName).toBe(data.expected.firstName); + expect(cipher.identity.middleName).toBe(data.expected.middleName); + expect(cipher.identity.lastName).toBe(data.expected.lastName); + }); + }); + + it("should parse secureNote records", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectSecureNote(cipher); + }); + + it("should parse an item and create a folder", async () => { + const result = await importer.parse(secureNoteData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.folders.length).toBe(1); + const folder = result.folders[0]; + expect(folder.name).toBe("notesFolder"); + }); +}); diff --git a/jslib/common/spec/importers/onepassword1PifImporter.spec.ts b/jslib/common/spec/importers/onepassword1PifImporter.spec.ts new file mode 100644 index 00000000..ffa2417a --- /dev/null +++ b/jslib/common/spec/importers/onepassword1PifImporter.spec.ts @@ -0,0 +1,527 @@ +import { FieldType } from "jslib-common/enums/fieldType"; +import { OnePassword1PifImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PifImporter"; + +const TestData: string = + "***aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee***\n" + + JSON.stringify({ + uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + updatedAt: 1486071244, + securityLevel: "SL5", + contentsHash: "aaaaaaaa", + title: "Imported Entry", + location: "https://www.google.com", + secureContents: { + fields: [ + { + value: "user@test.net", + id: "email-input", + name: "email", + type: "T", + designation: "username", + }, + { + value: "myservicepassword", + id: "password-input", + name: "password", + type: "P", + designation: "password", + }, + ], + sections: [ + { + fields: [ + { + k: "concealed", + n: "AAAAAAAAAAAABBBBBBBBBBBCCCCCCCCC", + v: "console-password-123", + t: "console password", + }, + ], + title: "Admin Console", + name: "admin_console", + }, + ], + passwordHistory: [ + { + value: "old-password", + time: 1447791421, + }, + ], + }, + URLs: [ + { + label: "website", + url: "https://www.google.com", + }, + ], + txTimestamp: 1508941334, + createdAt: 1390426636, + typeName: "webforms.WebForm", + }); + +const WindowsOpVaultTestData = JSON.stringify({ + category: "001", + created: 1544823719, + hmac: "NtyBmTTPOb88HV3JUKPx1xl/vcMhac9kvCfe/NtszY0=", + k: "**REMOVED LONG LINE FOR LINTER** -Kyle", + tx: 1553395669, + updated: 1553395669, + uuid: "528AB076FB5F4FBF960884B8E01619AC", + overview: { + title: "Google", + URLs: [ + { + u: "google.com", + }, + ], + url: "google.com", + ps: 26, + ainfo: "googluser", + }, + details: { + passwordHistory: [ + { + value: "oldpass1", + time: 1553394449, + }, + { + value: "oldpass2", + time: 1553394457, + }, + { + value: "oldpass3", + time: 1553394458, + }, + { + value: "oldpass4", + time: 1553394459, + }, + { + value: "oldpass5", + time: 1553394460, + }, + { + value: "oldpass6", + time: 1553394461, + }, + ], + fields: [ + { + type: "T", + id: "username", + name: "username", + value: "googluser", + designation: "username", + }, + { + type: "P", + id: "password", + name: "password", + value: "12345678901", + designation: "password", + }, + ], + notesPlain: "This is a note\r\n\r\nline1\r\nline2", + sections: [ + { + title: "test", + name: "1214FD88CD30405D9EED14BEB4D61B60", + fields: [ + { + k: "string", + n: "6CC3BD77482D4559A4B8BB2D360F821B", + v: "fgfg", + t: "fgggf", + }, + { + k: "concealed", + n: "5CFE7BCAA1DF4578BBF7EB508959BFF3", + v: "dfgdfgfdg", + t: "pwfield", + }, + ], + }, + ], + }, +}); + +const IdentityTestData = JSON.stringify({ + uuid: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + updatedAt: 1553365894, + securityLevel: "SL5", + contentsHash: "eeeeeeee", + title: "Test Identity", + secureContents: { + lastname: "Fritzenberger", + zip: "223344", + birthdate_dd: "11", + homephone: "+49 333 222 111", + company: "Web Inc.", + firstname: "Frank", + birthdate_mm: "3", + country: "de", + sex: "male", + sections: [ + { + fields: [ + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "firstname", + v: "Frank", + a: { + guarded: "yes", + }, + t: "first name", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "initial", + v: "MD", + a: { + guarded: "yes", + }, + t: "initial", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "lastname", + v: "Fritzenberger", + a: { + guarded: "yes", + }, + t: "last name", + }, + { + k: "menu", + v: "male", + n: "sex", + a: { + guarded: "yes", + }, + t: "sex", + }, + { + k: "date", + v: 1552305660, + n: "birthdate", + a: { + guarded: "yes", + }, + t: "birth date", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "occupation", + v: "Engineer", + a: { + guarded: "yes", + }, + t: "occupation", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "company", + v: "Web Inc.", + a: { + guarded: "yes", + }, + t: "company", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "department", + v: "IT", + a: { + guarded: "yes", + }, + t: "department", + }, + { + k: "string", + inputTraits: { + autocapitalization: "Words", + }, + n: "jobtitle", + v: "Developer", + a: { + guarded: "yes", + }, + t: "job title", + }, + ], + title: "Identification", + name: "name", + }, + { + fields: [ + { + k: "address", + inputTraits: { + autocapitalization: "Sentences", + }, + n: "address", + v: { + street: "Mainstreet 1", + city: "Berlin", + country: "de", + zip: "223344", + }, + a: { + guarded: "yes", + }, + t: "address", + }, + { + k: "phone", + v: "+49 001 222 333 44", + n: "defphone", + a: { + guarded: "yes", + }, + t: "default phone", + }, + { + k: "phone", + v: "+49 333 222 111", + n: "homephone", + a: { + guarded: "yes", + }, + t: "home", + }, + { + k: "phone", + n: "cellphone", + a: { + guarded: "yes", + }, + t: "mobile", + }, + { + k: "phone", + n: "busphone", + a: { + guarded: "yes", + }, + t: "business", + }, + ], + title: "Address", + name: "address", + }, + { + fields: [ + { + k: "string", + n: "username", + a: { + guarded: "yes", + }, + t: "username", + }, + { + k: "string", + n: "reminderq", + t: "reminder question", + }, + { + k: "string", + n: "remindera", + t: "reminder answer", + }, + { + k: "string", + inputTraits: { + keyboard: "EmailAddress", + }, + n: "email", + v: "test@web.de", + a: { + guarded: "yes", + }, + t: "email", + }, + { + k: "string", + n: "website", + inputTraits: { + keyboard: "URL", + }, + t: "website", + }, + { + k: "string", + n: "icq", + t: "ICQ", + }, + { + k: "string", + n: "skype", + t: "skype", + }, + { + k: "string", + n: "aim", + t: "AOL/AIM", + }, + { + k: "string", + n: "yahoo", + t: "Yahoo", + }, + { + k: "string", + n: "msn", + t: "MSN", + }, + { + k: "string", + n: "forumsig", + t: "forum signature", + }, + ], + title: "Internet Details", + name: "internet", + }, + { + title: "Related Items", + name: "linked items", + }, + ], + initial: "MD", + address1: "Mainstreet 1", + city: "Berlin", + jobtitle: "Developer", + occupation: "Engineer", + department: "IT", + email: "test@web.de", + birthdate_yy: "2019", + homephone_local: "+49 333 222 111", + defphone_local: "+49 001 222 333 44", + defphone: "+49 001 222 333 44", + }, + txTimestamp: 1553365894, + createdAt: 1553364679, + typeName: "identities.Identity", +}); + +describe("1Password 1Pif Importer", () => { + it("should parse data", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.login.username).toEqual("user@test.net"); + expect(cipher.login.password).toEqual("myservicepassword"); + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://www.google.com"); + }); + + it('should create concealed field as "hidden" type', async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + const fields = cipher.fields; + expect(fields.length).toEqual(1); + + const field = fields.shift(); + expect(field.name).toEqual("console password"); + expect(field.value).toEqual("console-password-123"); + expect(field.type).toEqual(FieldType.Hidden); + }); + + it("should create identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(IdentityTestData); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Test Identity"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Frank"); + expect(identity.middleName).toEqual("MD"); + expect(identity.lastName).toEqual("Fritzenberger"); + expect(identity.company).toEqual("Web Inc."); + expect(identity.address1).toEqual("Mainstreet 1"); + expect(identity.country).toEqual("DE"); + expect(identity.city).toEqual("Berlin"); + expect(identity.postalCode).toEqual("223344"); + expect(identity.phone).toEqual("+49 001 222 333 44"); + expect(identity.email).toEqual("test@web.de"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(6); + const fields = cipher.fields; + expect(fields[0].name).toEqual("sex"); + expect(fields[0].value).toEqual("male"); + expect(fields[1].name).toEqual("birth date"); + expect(fields[1].value).toEqual("Mon, 11 Mar 2019 12:01:00 GMT"); + expect(fields[2].name).toEqual("occupation"); + expect(fields[2].value).toEqual("Engineer"); + expect(fields[3].name).toEqual("department"); + expect(fields[3].value).toEqual("IT"); + expect(fields[4].name).toEqual("job title"); + expect(fields[4].value).toEqual("Developer"); + expect(fields[5].name).toEqual("home"); + expect(fields[5].value).toEqual("+49 333 222 111"); + }); + + it("should create password history", async () => { + const importer = new Importer(); + const result = await importer.parse(TestData); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(1); + const ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("old-password"); + expect(ph.lastUsedDate.toISOString()).toEqual("2015-11-17T20:17:01.000Z"); + }); + + it("should create password history from windows opvault 1pif format", async () => { + const importer = new Importer(); + const result = await importer.parse(WindowsOpVaultTestData); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(5); + let ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass6"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:41.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass5"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:40.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass4"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:39.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass3"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:38.000Z"); + ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("oldpass2"); + expect(ph.lastUsedDate.toISOString()).toEqual("2019-03-24T02:27:37.000Z"); + }); +}); diff --git a/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts b/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts new file mode 100644 index 00000000..a56e080a --- /dev/null +++ b/jslib/common/spec/importers/onepassword1PuxImporter.spec.ts @@ -0,0 +1,689 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { SecureNoteType } from "jslib-common/enums/secureNoteType"; +import { OnePassword1PuxImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepassword1PuxImporter"; +import { Utils } from "jslib-common/misc/utils"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +import { APICredentialsData } from "./testData/onePassword1Pux/APICredentials"; +import { BankAccountData } from "./testData/onePassword1Pux/BankAccount"; +import { CreditCardData } from "./testData/onePassword1Pux/CreditCard"; +import { DatabaseData } from "./testData/onePassword1Pux/Database"; +import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense"; +import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount"; +import { EmailFieldData } from "./testData/onePassword1Pux/Emailfield"; +import { EmailFieldOnIdentityData } from "./testData/onePassword1Pux/EmailfieldOnIdentity"; +import { EmailFieldOnIdentityPrefilledData } from "./testData/onePassword1Pux/EmailfieldOnIdentity_Prefilled"; +import { IdentityData } from "./testData/onePassword1Pux/IdentityData"; +import { LoginData } from "./testData/onePassword1Pux/LoginData"; +import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord"; +import { MembershipData } from "./testData/onePassword1Pux/Membership"; +import { OnePuxExampleFile } from "./testData/onePassword1Pux/Onepux_example"; +import { OutdoorLicenseData } from "./testData/onePassword1Pux/OutdoorLicense"; +import { PassportData } from "./testData/onePassword1Pux/Passport"; +import { PasswordData } from "./testData/onePassword1Pux/Password"; +import { RewardsProgramData } from "./testData/onePassword1Pux/RewardsProgram"; +import { SSNData } from "./testData/onePassword1Pux/SSN"; +import { SanitizedExport } from "./testData/onePassword1Pux/SanitizedExport"; +import { SecureNoteData } from "./testData/onePassword1Pux/SecureNote"; +import { ServerData } from "./testData/onePassword1Pux/Server"; +import { SoftwareLicenseData } from "./testData/onePassword1Pux/SoftwareLicense"; +import { WirelessRouterData } from "./testData/onePassword1Pux/WirelessRouter"; + +function validateCustomField(fields: FieldView[], fieldName: string, expectedValue: any) { + expect(fields).toBeDefined(); + const customField = fields.find((f) => f.name === fieldName); + expect(customField).toBeDefined(); + + expect(customField.value).toEqual(expectedValue); +} + +describe("1Password 1Pux Importer", () => { + const OnePuxExampleFileJson = JSON.stringify(OnePuxExampleFile); + const LoginDataJson = JSON.stringify(LoginData); + const CreditCardDataJson = JSON.stringify(CreditCardData); + const IdentityDataJson = JSON.stringify(IdentityData); + const SecureNoteDataJson = JSON.stringify(SecureNoteData); + const SanitizedExportJson = JSON.stringify(SanitizedExport); + + it("should parse login data", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("eToro"); + + expect(cipher.login.username).toEqual("username123123123@gmail.com"); + expect(cipher.login.password).toEqual("password!"); + expect(cipher.login.uris.length).toEqual(1); + expect(cipher.login.uri).toEqual("https://www.fakesite.com"); + expect(cipher.login.totp).toEqual("otpseed777"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(3); + validateCustomField(cipher.fields, "terms", "false"); + validateCustomField(cipher.fields, "policies", "true"); + validateCustomField(cipher.fields, "cyqyggt2otns6tbbqtsl6w2ceu", "username123123"); + }); + + it("should parse notes", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.notes).toEqual("This is a note. *bold*! _italic_!"); + }); + + it("should set favourite if favIndex equals 1", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.favorite).toBe(true); + }); + + it("should handle custom boolean fields", async () => { + const importer = new Importer(); + const result = await importer.parse(LoginDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + expect(cipher.fields[0].name).toEqual("terms"); + expect(cipher.fields[0].value).toEqual("false"); + expect(cipher.fields[0].type).toBe(FieldType.Boolean); + + expect(cipher.fields[1].name).toEqual("policies"); + expect(cipher.fields[1].value).toEqual("true"); + expect(cipher.fields[1].type).toBe(FieldType.Boolean); + }); + + it("should add fields of type email as custom fields", async () => { + const importer = new Importer(); + const EmailFieldDataJson = JSON.stringify(EmailFieldData); + const result = await importer.parse(EmailFieldDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + expect(cipher.fields[0].name).toEqual("reg_email"); + expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[1].name).toEqual("provider"); + expect(cipher.fields[1].value).toEqual("myEmailProvider"); + expect(cipher.fields[1].type).toBe(FieldType.Text); + }); + + it('should create concealed field as "hidden" type', async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + + const cipher = ciphers.shift(); + const fields = cipher.fields; + expect(fields.length).toEqual(1); + + const field = fields.shift(); + expect(field.name).toEqual("PIN"); + expect(field.value).toEqual("12345"); + expect(field.type).toEqual(FieldType.Hidden); + }); + + it("should create password history", async () => { + const importer = new Importer(); + const result = await importer.parse(OnePuxExampleFileJson); + const cipher = result.ciphers.shift(); + + expect(cipher.passwordHistory.length).toEqual(1); + const ph = cipher.passwordHistory.shift(); + expect(ph.password).toEqual("12345password"); + expect(ph.lastUsedDate.toISOString()).toEqual("2016-03-18T17:32:35.000Z"); + }); + + it("should create credit card records", async () => { + const importer = new Importer(); + const result = await importer.parse(CreditCardDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Parent's Credit Card"); + expect(cipher.notes).toEqual("My parents' credit card."); + + const card = cipher.card; + expect(card.cardholderName).toEqual("Fred Engels"); + expect(card.number).toEqual("6011111111111117"); + expect(card.code).toEqual("1312"); + expect(card.brand).toEqual("Discover"); + expect(card.expMonth).toEqual("12"); + expect(card.expYear).toEqual("2099"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(12); + validateCustomField(cipher.fields, "txbzvwzpck7ejhfres3733rbpm", "card"); + validateCustomField(cipher.fields, "cashLimit", "$500"); + validateCustomField(cipher.fields, "creditLimit", "$1312"); + validateCustomField(cipher.fields, "validFrom", "200101"); + validateCustomField(cipher.fields, "bank", "Some bank"); + validateCustomField(cipher.fields, "phoneLocal", "123456"); + validateCustomField(cipher.fields, "phoneTollFree", "0800123456"); + validateCustomField(cipher.fields, "phoneIntl", "+49123456"); + validateCustomField(cipher.fields, "website", "somebank.com"); + validateCustomField(cipher.fields, "pin", "1234"); + validateCustomField(cipher.fields, "interest", "1%"); + validateCustomField(cipher.fields, "issuenumber", "123456"); + }); + + it("should create identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(IdentityDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("George Engels"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toEqual("S"); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("Acme Inc."); + expect(identity.address1).toEqual("1312 Main St."); + expect(identity.country).toEqual("US"); + expect(identity.state).toEqual("California"); + expect(identity.city).toEqual("Atlantis"); + expect(identity.postalCode).toEqual("90210"); + expect(identity.phone).toEqual("4565555555"); + expect(identity.email).toEqual("gengels@nullvalue.test"); + expect(identity.username).toEqual("gengels"); + + // remaining fields as custom fields + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "birthdate", "Thu, 01 Jan 1981 12:01:00 GMT"); + validateCustomField(cipher.fields, "occupation", "Steel Worker"); + validateCustomField(cipher.fields, "department", "QA"); + validateCustomField(cipher.fields, "jobtitle", "Quality Assurance Manager"); + validateCustomField(cipher.fields, "homephone", "4575555555"); + validateCustomField(cipher.fields, "cellphone", "4585555555"); + validateCustomField(cipher.fields, "busphone", "4595555555"); + validateCustomField(cipher.fields, "reminderq", "Who's a super cool guy?"); + validateCustomField(cipher.fields, "remindera", "Me, buddy."); + validateCustomField(cipher.fields, "website", "cv.gengels.nullvalue.test"); + validateCustomField(cipher.fields, "icq", "12345678"); + validateCustomField(cipher.fields, "skype", "skypeisbad1619"); + validateCustomField(cipher.fields, "aim", "aollol@lololol.aol.com"); + validateCustomField(cipher.fields, "yahoo", "sk8rboi13@yah00.com"); + validateCustomField(cipher.fields, "msn", "msnothankyou@msn&m&m.com"); + validateCustomField(cipher.fields, "forumsig", "super cool guy"); + }); + + it("emails fields on identity types should be added to the identity email field", async () => { + const importer = new Importer(); + const EmailFieldOnIdentityDataJson = JSON.stringify(EmailFieldOnIdentityData); + const result = await importer.parse(EmailFieldOnIdentityDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + const identity = cipher.identity; + expect(identity.email).toEqual("gengels@nullvalue.test"); + + expect(cipher.fields[0].name).toEqual("provider"); + expect(cipher.fields[0].value).toEqual("myEmailProvider"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + }); + + it("emails fields on identity types should be added to custom fields if identity.email has been filled", async () => { + const importer = new Importer(); + const EmailFieldOnIdentityPrefilledDataJson = JSON.stringify(EmailFieldOnIdentityPrefilledData); + const result = await importer.parse(EmailFieldOnIdentityPrefilledDataJson); + expect(result != null).toBe(true); + + const ciphers = result.ciphers; + expect(ciphers.length).toEqual(1); + const cipher = ciphers.shift(); + + const identity = cipher.identity; + expect(identity.email).toEqual("gengels@nullvalue.test"); + + expect(cipher.fields[0].name).toEqual("2nd_email"); + expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test"); + expect(cipher.fields[0].type).toBe(FieldType.Text); + + expect(cipher.fields[1].name).toEqual("provider"); + expect(cipher.fields[1].value).toEqual("myEmailProvider"); + expect(cipher.fields[1].type).toBe(FieldType.Text); + }); + + it("should parse category 005 - Password (Legacy)", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PasswordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("SuperSecret Password"); + expect(cipher.notes).toEqual("SuperSecret Password Notes"); + + expect(cipher.login.password).toEqual("GBq[AGb]4*Si3tjwuab^"); + expect(cipher.login.uri).toEqual("https://n0t.y0ur.n0rm4l.w3bs1t3"); + }); + + it("should parse category 100 - SoftwareLicense", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SoftwareLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Limux Product Key"); + expect(cipher.notes).toEqual("My Software License"); + + expect(cipher.fields.length).toEqual(13); + validateCustomField(cipher.fields, "product_version", "5.10.1000"); + validateCustomField(cipher.fields, "reg_code", "265453-13457355-847327"); + validateCustomField(cipher.fields, "reg_name", "Kay Riddler"); + validateCustomField(cipher.fields, "reg_email", "kriddler@nullvalue.test"); + validateCustomField(cipher.fields, "company", "Riddles and Jigsaw Puzzles GmbH"); + validateCustomField( + cipher.fields, + "download_link", + "https://limuxcompany.nullvalue.test/5.10.1000/isos" + ); + validateCustomField(cipher.fields, "publisher_name", "Limux Software and Hardware"); + validateCustomField(cipher.fields, "publisher_website", "https://limuxcompany.nullvalue.test/"); + validateCustomField(cipher.fields, "retail_price", "$999"); + validateCustomField(cipher.fields, "support_email", "support@nullvalue.test"); + validateCustomField(cipher.fields, "order_date", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "order_number", "594839"); + validateCustomField(cipher.fields, "order_total", "$1086.59"); + }); + + it("should parse category 101 - BankAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(BankAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.Card); + expect(cipher.name).toEqual("Bank Account"); + expect(cipher.notes).toEqual("My Bank Account"); + + expect(cipher.card.cardholderName).toEqual("Cool Guy"); + + expect(cipher.fields.length).toEqual(9); + validateCustomField(cipher.fields, "bankName", "Super Credit Union"); + validateCustomField(cipher.fields, "accountType", "checking"); + validateCustomField(cipher.fields, "routingNo", "111000999"); + validateCustomField(cipher.fields, "accountNo", "192837465918273645"); + validateCustomField(cipher.fields, "swift", "123456"); + validateCustomField(cipher.fields, "iban", "DE12 123456"); + validateCustomField(cipher.fields, "telephonePin", "5555"); + validateCustomField(cipher.fields, "branchPhone", "9399399933"); + validateCustomField(cipher.fields, "branchAddress", "1 Fifth Avenue"); + }); + + it("should parse category 102 - Database", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DatabaseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Database"); + expect(cipher.notes).toEqual("My Database"); + + const login = cipher.login; + expect(login.username).toEqual("cooldbuser"); + expect(login.password).toEqual("^+kTjhLaN7wVPAhGU)*J"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "database_type", "postgresql"); + validateCustomField(cipher.fields, "hostname", "my.secret.db.server"); + validateCustomField(cipher.fields, "port", "1337"); + validateCustomField(cipher.fields, "database", "user_database"); + validateCustomField(cipher.fields, "sid", "ASDIUFU-283234"); + validateCustomField(cipher.fields, "alias", "cdbu"); + validateCustomField(cipher.fields, "options", "ssh"); + }); + + it("should parse category 103 - Drivers license", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(DriversLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Michael Scarn"); + expect(cipher.subTitle).toEqual("Michael Scarn"); + expect(cipher.notes).toEqual("My Driver's License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Michael"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Scarn"); + expect(identity.address1).toEqual("2120 Mifflin Rd."); + expect(identity.state).toEqual("Pennsylvania"); + expect(identity.country).toEqual("United States"); + expect(identity.licenseNumber).toEqual("12345678901"); + + expect(cipher.fields.length).toEqual(6); + validateCustomField(cipher.fields, "birthdate", "Sun, 01 Jan 1978 12:01:00 GMT"); + validateCustomField(cipher.fields, "sex", "male"); + validateCustomField(cipher.fields, "height", "5'11\""); + validateCustomField(cipher.fields, "class", "C"); + validateCustomField(cipher.fields, "conditions", "B"); + validateCustomField(cipher.fields, "expiry_date", "203012"); + }); + + it("should parse category 104 - Outdoor License", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(OutdoorLicenseData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Harvest License"); + expect(cipher.subTitle).toEqual("Cash Bandit"); + expect(cipher.notes).toEqual("My Outdoor License"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Cash"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Bandit"); + expect(identity.state).toEqual("Washington"); + expect(identity.country).toEqual("United States of America"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "valid_from", "Thu, 01 Apr 2021 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Fri, 01 Apr 2044 12:01:00 GMT"); + validateCustomField(cipher.fields, "game", "Bananas,blueberries,corn"); + validateCustomField(cipher.fields, "quota", "100/each"); + }); + + it("should parse category 105 - Membership", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MembershipData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Library Card"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("George"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Engels"); + expect(identity.company).toEqual("National Public Library"); + expect(identity.phone).toEqual("9995555555"); + + expect(cipher.fields.length).toEqual(5); + validateCustomField(cipher.fields, "website", "https://npl.nullvalue.gov.test"); + validateCustomField(cipher.fields, "member_since", "199901"); + validateCustomField(cipher.fields, "expiry_date", "203412"); + validateCustomField(cipher.fields, "membership_no", "64783862"); + validateCustomField(cipher.fields, "pin", "19191"); + }); + + it("should parse category 106 - Passport", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(PassportData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Mr. Globewide"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("David"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Global"); + expect(identity.passportNumber).toEqual("76436847"); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "type", "US Passport"); + validateCustomField(cipher.fields, "sex", "female"); + validateCustomField(cipher.fields, "nationality", "International"); + validateCustomField(cipher.fields, "issuing_authority", "Department of State"); + validateCustomField(cipher.fields, "birthdate", "Fri, 01 Apr 1983 12:01:00 GMT"); + validateCustomField(cipher.fields, "birthplace", "A cave somewhere in Maine"); + validateCustomField(cipher.fields, "issue_date", "Wed, 01 Jan 2020 12:01:00 GMT"); + validateCustomField(cipher.fields, "expiry_date", "Sat, 01 Jan 2050 12:01:00 GMT"); + }); + + it("should parse category 107 - RewardsProgram", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(RewardsProgramData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Identity); + expect(cipher.name).toEqual("Retail Reward Thing"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Chef"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Coldroom"); + expect(identity.company).toEqual("Super Cool Store Co."); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "membership_no", "member-29813569"); + validateCustomField(cipher.fields, "pin", "99913"); + validateCustomField(cipher.fields, "additional_no", "additional member id"); + validateCustomField(cipher.fields, "member_since", "202101"); + validateCustomField(cipher.fields, "customer_service_phone", "123456"); + validateCustomField(cipher.fields, "reservations_phone", "123456"); + validateCustomField(cipher.fields, "website", "supercoolstore.com"); + }); + + it("should parse category 108 - SSN", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(SSNData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("SSN"); + + const identity = cipher.identity; + expect(identity.firstName).toEqual("Jack"); + expect(identity.middleName).toBeNull(); + expect(identity.lastName).toEqual("Judd"); + expect(identity.ssn).toEqual("131-216-1900"); + }); + + it("should parse category 109 - WirelessRouter", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(WirelessRouterData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Wireless Router"); + expect(cipher.notes).toEqual("My Wifi Router Config"); + + expect(cipher.login.password).toEqual("BqatGTVQ9TCN72tLbjrsHqkb"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField(cipher.fields, "name", "pixel 2Xl"); + validateCustomField(cipher.fields, "server", "127.0.0.1"); + validateCustomField(cipher.fields, "airport_id", "some airportId"); + validateCustomField(cipher.fields, "network_name", "some network name"); + validateCustomField(cipher.fields, "wireless_security", "WPA"); + validateCustomField(cipher.fields, "wireless_password", "wifipassword"); + validateCustomField(cipher.fields, "disk_password", "diskpassword"); + }); + + it("should parse category 110 - Server", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(ServerData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("Super Cool Server"); + expect(cipher.notes).toEqual("My Server"); + + expect(cipher.login.username).toEqual("frankly-notsure"); + expect(cipher.login.password).toEqual("*&YHJI87yjy78u"); + expect(cipher.login.uri).toEqual("https://coolserver.nullvalue.test"); + + expect(cipher.fields.length).toEqual(7); + validateCustomField( + cipher.fields, + "admin_console_url", + "https://coolserver.nullvalue.test/admin" + ); + validateCustomField(cipher.fields, "admin_console_username", "frankly-idontknowwhatimdoing"); + validateCustomField(cipher.fields, "admin_console_password", "^%RY&^YUiju8iUYHJI(U"); + validateCustomField(cipher.fields, "name", "Private Hosting Provider Inc."); + validateCustomField(cipher.fields, "website", "https://phpi.nullvalue.test"); + validateCustomField( + cipher.fields, + "support_contact_url", + "https://phpi.nullvalue.test/support" + ); + validateCustomField(cipher.fields, "support_contact_phone", "8882569382"); + }); + + it("should parse category 111 - EmailAccount", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(EmailAccountData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Email Config"); + expect(cipher.notes).toEqual("My Email Config"); + + expect(cipher.fields.length).toEqual(17); + validateCustomField(cipher.fields, "pop_type", "either"); + validateCustomField(cipher.fields, "pop_username", "someuser@nullvalue.test"); + validateCustomField(cipher.fields, "pop_server", "mailserver.nullvalue.test"); + validateCustomField(cipher.fields, "pop_port", "587"); + validateCustomField(cipher.fields, "pop_password", "u1jsf { + const importer = new Importer(); + const jsonString = JSON.stringify(APICredentialsData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.name).toEqual("API Credential"); + expect(cipher.notes).toEqual("My API Credential"); + + expect(cipher.login.username).toEqual("apiuser@nullvalue.test"); + expect(cipher.login.password).toEqual("apiapiapiapiapiapiappy"); + expect(cipher.login.uri).toEqual("http://not.your.everyday.hostname"); + + expect(cipher.fields.length).toEqual(4); + validateCustomField(cipher.fields, "type", "jwt"); + validateCustomField(cipher.fields, "filename", "filename.jwt"); + validateCustomField(cipher.fields, "validFrom", "Mon, 04 Apr 2011 12:01:00 GMT"); + validateCustomField(cipher.fields, "expires", "Tue, 01 Apr 2031 12:01:00 GMT"); + }); + + it("should create secure notes", async () => { + const importer = new Importer(); + const result = await importer.parse(SecureNoteDataJson); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("Secure Note #1"); + expect(cipher.notes).toEqual( + "This is my secure note. \n\nLorem ipsum expecto patronum. \nThe quick brown fox jumped over the lazy dog." + ); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + }); + + it("should parse category 113 - Medical Record", async () => { + const importer = new Importer(); + const jsonString = JSON.stringify(MedicalRecordData); + const result = await importer.parse(jsonString); + expect(result != null).toBe(true); + const cipher = result.ciphers.shift(); + expect(cipher.type).toEqual(CipherType.SecureNote); + expect(cipher.name).toEqual("Some Health Record"); + expect(cipher.notes).toEqual("Some notes about my medical history"); + expect(cipher.secureNote.type).toEqual(SecureNoteType.Generic); + + expect(cipher.fields.length).toEqual(8); + validateCustomField(cipher.fields, "date", "Sat, 01 Jan 2022 12:01:00 GMT"); + validateCustomField(cipher.fields, "location", "some hospital/clinic"); + validateCustomField(cipher.fields, "healthcareprofessional", "Some Doctor"); + validateCustomField(cipher.fields, "patient", "Me"); + validateCustomField(cipher.fields, "reason", "unwell"); + validateCustomField(cipher.fields, "medication", "Insuline"); + validateCustomField(cipher.fields, "dosage", "1"); + validateCustomField(cipher.fields, "notes", "multiple times a day"); + }); + + it("should create folders", async () => { + const importer = new Importer(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const folders = result.folders; + expect(folders.length).toBe(5); + expect(folders[0].name).toBe("Movies"); + expect(folders[1].name).toBe("Finance"); + expect(folders[2].name).toBe("Travel"); + expect(folders[3].name).toBe("Education"); + expect(folders[4].name).toBe("Starter Kit"); + + // Check that ciphers have a folder assigned to them + expect(result.ciphers.filter((c) => c.folderId === folders[0].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[1].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[2].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[3].id).length).toBeGreaterThan(0); + expect(result.ciphers.filter((c) => c.folderId === folders[4].id).length).toBeGreaterThan(0); + }); + + it("should create collections if part of an organization", async () => { + const importer = new Importer(); + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(SanitizedExportJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(5); + expect(collections[0].name).toBe("Movies"); + expect(collections[1].name).toBe("Finance"); + expect(collections[2].name).toBe("Travel"); + expect(collections[3].name).toBe("Education"); + expect(collections[4].name).toBe("Starter Kit"); + }); +}); diff --git a/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts b/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts new file mode 100644 index 00000000..73569fc6 --- /dev/null +++ b/jslib/common/spec/importers/onepasswordMacCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { OnePasswordMacCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordMacCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; + +import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.mac.csv"; +import { data as identityData } from "./testData/onePasswordCsv/identity.mac.csv"; +import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.mac.csv"; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual( + expect.objectContaining({ + firstName: "first name", + middleName: "mi", + lastName: "last name", + username: "userNam3", + company: "bitwarden", + phone: "8005555555", + email: "email@bitwarden.com", + }) + ); + + expect(cipher.notes).toContain("address\ncity state zip\nUnited States"); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual( + expect.objectContaining({ + number: "4111111111111111", + code: "111", + cardholderName: "test", + expMonth: "1", + expYear: "2030", + }) + ); +} + +describe("1Password mac CSV Importer", () => { + it("should parse identity records", async () => { + const importer = new Importer(); + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it("should parse credit card records", async () => { + const importer = new Importer(); + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse csv's with multiple record type", async () => { + const importer = new Importer(); + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts b/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts new file mode 100644 index 00000000..bfc1044a --- /dev/null +++ b/jslib/common/spec/importers/onepasswordWinCsvImporter.spec.ts @@ -0,0 +1,87 @@ +import { CipherType } from "jslib-common/enums/cipherType"; +import { FieldType } from "jslib-common/enums/fieldType"; +import { OnePasswordWinCsvImporter as Importer } from "jslib-common/importers/onepasswordImporters/onepasswordWinCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { FieldView } from "jslib-common/models/view/fieldView"; + +import { data as creditCardData } from "./testData/onePasswordCsv/creditCard.windows.csv"; +import { data as identityData } from "./testData/onePasswordCsv/identity.windows.csv"; +import { data as multiTypeData } from "./testData/onePasswordCsv/multipleItems.windows.csv"; + +function expectIdentity(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Identity); + + expect(cipher.identity).toEqual( + expect.objectContaining({ + firstName: "first name", + middleName: "mi", + lastName: "last name", + username: "userNam3", + company: "bitwarden", + phone: "8005555555", + email: "email@bitwarden.com", + }) + ); + + expect(cipher.fields).toEqual( + expect.arrayContaining([ + Object.assign(new FieldView(), { + type: FieldType.Text, + name: "address", + value: "address city state zip us", + }), + ]) + ); +} + +function expectCreditCard(cipher: CipherView) { + expect(cipher.type).toBe(CipherType.Card); + + expect(cipher.card).toEqual( + expect.objectContaining({ + number: "4111111111111111", + code: "111", + cardholderName: "test", + expMonth: "1", + expYear: "1970", + }) + ); +} + +describe("1Password windows CSV Importer", () => { + let importer: Importer; + beforeEach(() => { + importer = new Importer(); + }); + + it("should parse identity records", async () => { + const result = await importer.parse(identityData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectIdentity(cipher); + }); + + it("should parse credit card records", async () => { + const result = await importer.parse(creditCardData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expectCreditCard(cipher); + }); + + it("should parse csv's with multiple record types", async () => { + const result = await importer.parse(multiTypeData); + + expect(result).not.toBeNull(); + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + + expectIdentity(result.ciphers[1]); + expectCreditCard(result.ciphers[2]); + }); +}); diff --git a/jslib/common/spec/importers/safariCsvImporter.spec.ts b/jslib/common/spec/importers/safariCsvImporter.spec.ts new file mode 100644 index 00000000..f1e1186f --- /dev/null +++ b/jslib/common/spec/importers/safariCsvImporter.spec.ts @@ -0,0 +1,74 @@ +import { SafariCsvImporter as Importer } from "jslib-common/importers/safariCsvImporter"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginUriView } from "jslib-common/models/view/loginUriView"; +import { LoginView } from "jslib-common/models/view/loginView"; + +import { data as oldSimplePasswordData } from "./testData/safariCsv/oldSimplePasswordData.csv"; +import { data as simplePasswordData } from "./testData/safariCsv/simplePasswordData.csv"; + +const CipherData = [ + { + title: "should parse URLs in new CSV format", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com (example_user)", + login: Object.assign(new LoginView(), { + username: "example_user", + password: "example_p@ssword", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + totp: "otpauth://totp/test?secret=examplesecret", + }), + notes: "Example note\nMore notes on new line", + type: 1, + }), + }, + { + title: "should parse URLs in old CSV format", + csv: oldSimplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "example.com (example_user)", + login: Object.assign(new LoginView(), { + username: "example_user", + password: "example_p@ssword", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + type: 1, + }), + }, +]; + +describe("Safari CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + // eslint-disable-next-line + if (data.expected.hasOwnProperty(property)) { + // eslint-disable-next-line + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts b/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts new file mode 100644 index 00000000..43ab8c76 --- /dev/null +++ b/jslib/common/spec/importers/testData/bitwardenJson/empty.json.ts @@ -0,0 +1 @@ +export const data = '{"encrypted":false,"folders":[],"items":[]}'; diff --git a/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts b/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts new file mode 100644 index 00000000..0462e834 --- /dev/null +++ b/jslib/common/spec/importers/testData/bitwardenJson/passwordProtected.json.ts @@ -0,0 +1,9 @@ +export const data = `{ + "encrypted": true, + "passwordProtected": true, + "salt": "Oy0xcgVRzxQ+9NpB5GLehw==", + "kdfIterations": 100000, + "kdfType": 0, + "encKeyValidation_DO_NOT_EDIT": "2.sZs4Jc1HW9rhABzRRYR/gQ==|8kTDaDxafulnybpWoqVX8RAybhVRTr+dffNjms271Y7amQmIE1VSMwLbk+b2vxZb|IqOo6oXQtmv/Xb/GHDi42XG9c9ILePYtP5qq584VWcg=", + "data": "2.D0AXAf7G/XIwq6EC7A0Suw==|4w+m0wHRo25y1T1Syh5wdAUyF8voqEy54waMEsbnK0Nzee959w54ru5D1NntvxZL4HFqkQLyR6jCFkn5g40f+MGJgihS/wvf4NcJJfLiiFo6MEDOQNBkxw7ZBGuHiKfVuBO5u36JgzQtZ8lyFaduGxFszuF5c+URiE9PDh9jY0//poVgHKwuLZuYFIW+f7h6T+shUWK0ya11lcHn/B/CA2xiI+YiKdNZreJrwN0yslpJ/f+MrOzagvftRjt0GNkwveCtwcYUw/zFvqvibUpKeHcRiXs8SaGoHJ5RTm69FbJ7C5tnLwoVT89Af156uvRAXV7yAC4oPcbU/3TGb6hqYosvi1QNyaqG3M9gxS6+AK0C4yWuNbMLDEr+MWiw0SWLVMKQEkCZ4oM+oTCx52otW3+2V9I8Pv3KmmhkvVvE4wBdweOJeRX53Tf5ySkmpIhCfzj6JMmxO+nmTXIhWnJChr4hPVh+ixv1GQK5thIPTCMXmAtXoTIFUx1KWjS6LjOdi2hKQueVI+XZjf0qnY2vTMxRg0ZsLBA2znQTx+DSEqumORb5T/lV73pWZiCNePSAE2msOm7tep+lm4O/VCViCfXjITAY196syhOK0XnhxJvPALchZY8sYRAfuw6hHoDiVr+JUieRoI7eUrhXBp+D6Py9TL/dS/rHe+C2Zhx+xwx2NfGt+xEp8ZAOOCxgZ0UTeSA/abm0Oz7tJIK1n26acQrgbr7rMeBymAX+5L5OWlwI1hGgEBfj6W0rrbSXf3VMfaFXZ5UsXi1VhzQmU3LyWENoDeImXFQj6zMbUSfcVwLsG5Fg8Ee/kO/wJPfG5BO51+/vFqQj6AkaMEcwg5xNrObHYfQ/DMhIn7YDM2zdzbNTdhnobGkz6YRKFPCgFe3EmIEPEpeh9S3eKE9C7MQsrR8jVSiseR/FipJLsN+W7iOwzeXdwxUFlC/0a98bTKvdrbMgNi6ZVXykHY/t2UyEGpxZGTHoZwhX01kiQrwzC4/+v/676ldxPluO9GY7MtrLveCDsiyBz15u43IGHayDEBNT0rqrOKLYmfzwCWoahRLZQrSmepe/FXqgPqRfyWc/Ro+w3sT9dXUkx3B5xxWgSyABowPV48yBUSJuefhKTpqgzkU+LzhNnWHjnxJzzQ2/|IhlRjnyhIoDM85qHX/bY2zaIU5YaRO/iFVTQDd3uFDo=" +}`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts new file mode 100644 index 00000000..78e42cb1 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/credentials.csv.ts @@ -0,0 +1,2 @@ +export const credentialsData = `username,username2,username3,title,password,note,url,category,otpSecret +jdoe,,,example.com,somePassword,some note for example.com,https://www.example.com,Entertainment,someTOTPSeed`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts new file mode 100644 index 00000000..1c939557 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/id.csv.ts @@ -0,0 +1,6 @@ +export const identityData = `type,number,name,issue_date,expiration_date,place_of_issue,state +card,123123123,John Doe,2022-1-30,2032-1-30,, +passport,123123123,John Doe,2022-1-30,2032-1-30,somewhere in Germany, +license,1234556,John Doe,2022-8-10,2022-10-10,,DC +social_security,123123123,John Doe,,,, +tax_number,123123123,,,,,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts new file mode 100644 index 00000000..cca5748b --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/multiplePersonalInfo.csv.ts @@ -0,0 +1,7 @@ +export const multiplePersonalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url +name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,, +email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,, +number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,, +address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,, +website,,,,,,,,,,Website,,,,,,,,,,,,,website.com +name,Mrs,Jane,,Doe,jdoe,2022-01-30,earth,,,,,,,,,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts new file mode 100644 index 00000000..336986c1 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/payments.csv.ts @@ -0,0 +1,3 @@ +export const paymentsData = `type,account_name,account_holder,cc_number,code,expiration_month,expiration_year,routing_number,account_number,country,issuing_bank +bank,John's savings account,John Doe,,,,,routingNumber,accountNumber,US,US-ALLY +credit_card,John Doe,,41111111111111111,123,01,2023,,,US,`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts new file mode 100644 index 00000000..ec99f800 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/personalInfo.csv.ts @@ -0,0 +1,6 @@ +export const personalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url +name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,, +email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,, +number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,, +address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,, +website,,,,,,,,,,Website,,,,,,,,,,,,,website.com`; diff --git a/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts b/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts new file mode 100644 index 00000000..22a3b904 --- /dev/null +++ b/jslib/common/spec/importers/testData/dashlaneCsv/securenotes.csv.ts @@ -0,0 +1,2 @@ +export const secureNoteData = `title,note +01,test`; diff --git a/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts b/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts new file mode 100644 index 00000000..2f2c1b55 --- /dev/null +++ b/jslib/common/spec/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts @@ -0,0 +1,4 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +`; diff --git a/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts b/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts new file mode 100644 index 00000000..90e19f19 --- /dev/null +++ b/jslib/common/spec/importers/testData/firefoxCsv/simplePasswordData.csv.ts @@ -0,0 +1,2 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900"`; diff --git a/jslib/common/spec/importers/testData/keeperJson/testData.ts b/jslib/common/spec/importers/testData/keeperJson/testData.ts new file mode 100644 index 00000000..fee8f54c --- /dev/null +++ b/jslib/common/spec/importers/testData/keeperJson/testData.ts @@ -0,0 +1,90 @@ +import { KeeperJsonExport } from "jslib-common/importers/keeperImporters/types/keeperJsonTypes"; + +export const testData: KeeperJsonExport = { + shared_folders: [ + { + path: "My Customer 1", + manage_users: true, + manage_records: true, + can_edit: true, + can_share: true, + permissions: [ + { + uid: "kVM96KGEoGxhskZoSTd_jw", + manage_users: true, + manage_records: true, + }, + { + name: "user@mycompany.com", + manage_users: true, + manage_records: true, + }, + ], + }, + { + path: "Testing\\My Customer 2", + manage_users: true, + manage_records: true, + can_edit: true, + can_share: true, + permissions: [ + { + uid: "ih1CggiQ-3ENXcn4G0sl-g", + manage_users: true, + manage_records: true, + }, + { + name: "user@mycompany.com", + manage_users: true, + manage_records: true, + }, + ], + }, + ], + records: [ + { + title: "Bank Account 1", + login: "customer1234", + password: "4813fJDHF4239fdk", + login_url: "https://chase.com", + notes: "These are some notes.", + custom_fields: { + "Account Number": "123-456-789", + }, + folders: [ + { + folder: "Optional Private Folder 1", + }, + ], + }, + { + title: "Bank Account 2", + login: "mybankusername", + password: "w4k4k193f$^&@#*%2", + login_url: "https://amex.com", + notes: "Some great information here.", + custom_fields: { + "Security Group": "Public", + "IP Address": "12.45.67.8", + "TFC:Keeper": + "otpauth://totp/Amazon:me@company.com?secret=JBSWY3DPEHPK3PXP&issuer=Amazon&algorithm=SHA1&digits=6&period=30", + }, + folders: [ + { + folder: "Optional Private Folder 1", + }, + { + shared_folder: "My Customer 1", + can_edit: true, + can_share: true, + }, + ], + }, + { + title: "Some Account", + login: "someUserName", + password: "w4k4k1wergf$^&@#*%2", + login_url: "https://example.com", + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts new file mode 100644 index 00000000..5ccf0971 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserAccount.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userAccountData = `nickname,url,username,password,additionalInfo,twofaSecret,status,tags +PasswordNickname,www.google.com,user.name@email.com,abc123,This is the additional information text.,someTOTPSeed,active,someTag`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts new file mode 100644 index 00000000..0b127627 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserCreditCard.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userCreditCardData = `nickname,status,tags,cardNumber,cardName,exp_month,exp_year,cvv,additionalInfo +Visa test card,active,someTag,4111111111111111,Joe User,04,24,222,This is the additional information field`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts new file mode 100644 index 00000000..8f3fa600 --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserIdCard.csv.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export const userIdCardData = `nickname,status,tags,idType,idNumber,idName,idIssuanceDate,idExpirationDate,idCountry,additionalInfo +Joe User's nickname,active,someTag,Driver's License,123456,Joe M User,02/02/2022,02/02/2024,United States,Additional information +Passport ID card,active,someTag,Passport,1234567,Joe M User,03/07/2022,03/07/2028,United States,Additional information field +Social Security ID card,active,someTag,Social Security,123455678,Joe M User,03/07/2022,03/07/2028,United States,Additional information field text +ID card type ID card,active,someTag,ID Card,1234566,Joe M User,03/07/2022,03/07/2028,United States,Additional Information field text +Tax number ID card,active,someTag,Tax Number,12345678,Joe M User,03/07/2022,03/07/2028,United States,Additinoal information text field +Bank account ID card,active,someTag,Bank Account,12344556677,Joe M User,03/07/2022,03/07/2028,United States,Additional text information here +Insurance card ID card,active,someTag,Insurance Card,123456677,Joe M User,03/07/2022,03/07/2022,United States,Additional information text goes here +Health card Id card,active,someTag,Health Card,1234670,Joe M User,03/07/2022,03/07/2028,United States,More info +Membership ID card,active,someTag,Membership,12345709,Joe M User,03/07/2022,03/07/2028,United States,Add'l info +Database ID card,active,someTag,Database,12345089u,Joe M User,03/07/2022,03/07/2028,United States,Addin't info +Outdoor license ID card,active,someTag,Outdoor License,123890090,Joe M User,03/07/2022,03/07/2028,United States,Additional info +Reward program Id card,active,someTag,Reward Program,12345890b,Joe M User,03/07/2022,03/07/2028,United States,1234890 +Software license ID card,active,someTag,Software License,1234567c,Joe M User,03/07/2022,03/07/2028,United States,"It seems like the fields don't change, which makes it pretty useless that they have so many ID card types." +Tour visa ID card,active,someTag,Tour Visa,123456lkhj,Joe M User,03/07/2022,03/07/2028,United States,Additional Informaion text`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts new file mode 100644 index 00000000..13afb16d --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserIdentity.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userIdentityData = `nickname,status,tags,firstName,middleName,lastName,email,firstAddressLine,secondAddressLine,title,gender,number,city,country,zipCode,additionalInfo +Joe User's nickname,active,someTag,Joe,M,User,joe.user@email.com,1 Example House,Suite 300,Mr,Male,2223334444,Portland,United States,04101,Additional information field`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts new file mode 100644 index 00000000..8094c92f --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserNote.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userNoteData = `nickname,status,content +The title of a secure note,active,"The content of a secure note. Lorem ipsum, etc."`; diff --git a/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts b/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts new file mode 100644 index 00000000..68cfd44f --- /dev/null +++ b/jslib/common/spec/importers/testData/mykiCsv/UserTwofa.csv.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +export const userTwoFaData = `nickname,status,tags,authToken,additionalInfo +2FA nickname,active,someTag,someTOTPSeed,"Additional information field content. "`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts new file mode 100644 index 00000000..8d79d2b1 --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.card.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeVisa,,,,,SomeHolder,4024007103939509,123,01 / 22,12345,,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts new file mode 100644 index 00000000..4dde389c --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.identity.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeTitle,,,,SomeNoteToMyIdentity,,,,,123456,,#fullName,123456789,hello@bitwarden.com,Test street 123,additional addressinfo,Cologne,Germany,North-Rhine-Westphalia`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts new file mode 100644 index 00000000..e2af76b0 --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.login.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +SomeVaultItemName,https://example.com,hello@bitwarden.com,someStrongPassword,Some note for the VaultItem,,,,,,SomeFolderForVaultItem,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts new file mode 100644 index 00000000..0e4dcb2e --- /dev/null +++ b/jslib/common/spec/importers/testData/nordpassCsv/nordpass.secureNote.csv.ts @@ -0,0 +1,3 @@ +export const data = `name,url,username,password,note,cardholdername,cardnumber,cvc,expirydate,zipcode,folder,full_name,phone_number,email,address1,address2,city,country,state +notesFolder,,,,,,,,,,,,,,,,,, +MySuperSecureNoteTitle,,,,MySuperSecureNote,,,,,,notesFolder,,,,,,,,`; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts b/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts new file mode 100644 index 00000000..41b75331 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/APICredentials.ts @@ -0,0 +1,169 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const APICredentialsData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "6nqnjdqyk5mwvqbdgbdr47oabe", + favIndex: 0, + createdAt: 1619465969, + updatedAt: 1619466052, + trashed: false, + categoryUuid: "112", + details: { + loginFields: [], + notesPlain: "My API Credential", + sections: [ + { + title: "", + fields: [ + { + title: "username", + id: "username", + value: { + string: "apiuser@nullvalue.test", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "credential", + id: "credential", + value: { + concealed: "apiapiapiapiapiapiappy", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + { + title: "type", + id: "type", + value: { + menu: "jwt", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "filename", + id: "filename", + value: { + string: "filename.jwt", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + date: 1301918460, + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expires", + id: "expires", + value: { + date: 1932811260, + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "hostname", + id: "hostname", + value: { + string: "not.your.everyday.hostname", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "", + title: "API Credential", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts b/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts new file mode 100644 index 00000000..1ada2714 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/BankAccount.ts @@ -0,0 +1,224 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const BankAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "u2l4sjbencvsowwjuj3dfpt73q", + favIndex: 0, + createdAt: 1619466056, + updatedAt: 1619466187, + trashed: false, + categoryUuid: "101", + details: { + loginFields: [], + notesPlain: "My Bank Account", + sections: [ + { + title: "", + fields: [ + { + title: "bank name", + id: "bankName", + value: { + string: "Super Credit Union", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "name on account", + id: "owner", + value: { + string: "Cool Guy", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "accountType", + value: { + menu: "checking", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "routing number", + id: "routingNo", + value: { + string: "111000999", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "account number", + id: "accountNo", + value: { + string: "192837465918273645", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SWIFT", + id: "swift", + value: { + string: "123456", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "IBAN", + id: "iban", + value: { + string: "DE12 123456", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "PIN", + id: "telephonePin", + value: { + concealed: "5555", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Branch Information", + name: "branchInfo", + fields: [ + { + title: "phone", + id: "branchPhone", + value: { + phone: "9399399933", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "address", + id: "branchAddress", + value: { + string: "1 Fifth Avenue", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "Super Credit Union", + tags: ["Finance"], + title: "Bank Account", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts b/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts new file mode 100644 index 00000000..ccdd4aa9 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/CreditCard.ts @@ -0,0 +1,343 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const CreditCardData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "vpxi2esuujz7nrbojp34rd5aja", + favIndex: 0, + createdAt: 1619465282, + updatedAt: 1619465447, + trashed: false, + categoryUuid: "002", + details: { + loginFields: [], + notesPlain: "My parents' credit card. ", + sections: [ + { + title: "", + fields: [ + { + title: "cardholder name", + id: "cardholder", + value: { + string: "Fred Engels", + }, + indexAtSource: 0, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "type", + id: "type", + value: { + creditCardType: "discover", + }, + indexAtSource: 1, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "ccnum", + value: { + creditCardNumber: "6011111111111117", + }, + indexAtSource: 2, + guarded: true, + clipboardFilter: "0123456789", + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "verification number", + id: "cvv", + value: { + concealed: "1312", + }, + indexAtSource: 3, + guarded: true, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry", + value: { + monthYear: 209912, + }, + indexAtSource: 4, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "valid from", + id: "validFrom", + value: { + monthYear: 200101, + }, + indexAtSource: 5, + guarded: true, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "", + id: "txbzvwzpck7ejhfres3733rbpm", + value: { + string: "card", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Contact Information", + name: "contactInfo", + fields: [ + { + title: "issuing bank", + id: "bank", + value: { + string: "Some bank", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "phone (local)", + id: "phoneLocal", + value: { + phone: "123456", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (toll free)", + id: "phoneTollFree", + value: { + phone: "0800123456", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "phone (intl)", + id: "phoneIntl", + value: { + phone: "+49123456", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "namePhonePad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "website", + id: "website", + value: { + url: "somebank.com", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + { + title: "Additional Details", + name: "details", + fields: [ + { + title: "PIN", + id: "pin", + value: { + concealed: "1234", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: true, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "credit limit", + id: "creditLimit", + value: { + string: "$1312", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "cash withdrawal limit", + id: "cashLimit", + value: { + string: "$500", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "interest rate", + id: "interest", + value: { + string: "1%", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numbersAndPunctuation", + correction: "default", + capitalization: "default", + }, + }, + { + title: "issue number", + id: "issuenumber", + value: { + string: "123456", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "1234 **** 6789", + tags: ["Finance"], + title: "Parent's Credit Card", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts b/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts new file mode 100644 index 00000000..594f5384 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/Database.ts @@ -0,0 +1,201 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DatabaseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "ospvepl3ex2y6hjwwqwyvtf2sy", + favIndex: 0, + createdAt: 1619466193, + updatedAt: 1619466276, + trashed: false, + categoryUuid: "102", + details: { + loginFields: [], + notesPlain: "My Database", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "database_type", + value: { + menu: "postgresql", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "server", + id: "hostname", + value: { + string: "my.secret.db.server", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port", + id: "port", + value: { + string: "1337", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "database", + id: "database", + value: { + string: "user_database", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "username", + id: "username", + value: { + string: "cooldbuser", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "password", + id: "password", + value: { + concealed: "^+kTjhLaN7wVPAhGU)*J", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "SID", + id: "sid", + value: { + string: "ASDIUFU-283234", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "alias", + id: "alias", + value: { + string: "cdbu", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "connection options", + id: "options", + value: { + string: "ssh", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "my.secret.db.server", + title: "Database", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts b/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts new file mode 100644 index 00000000..468069ae --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/DriversLicense.ts @@ -0,0 +1,233 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const DriversLicenseData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "nntuge2g7s2wrlokyfhea354ay", + favIndex: 0, + createdAt: 1619466279, + updatedAt: 1619466425, + trashed: false, + categoryUuid: "103", + details: { + loginFields: [], + notesPlain: "My Driver's License", + sections: [ + { + title: "", + fields: [ + { + title: "full name", + id: "fullname", + value: { + string: "Michael Scarn", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "words", + }, + }, + { + title: "address", + id: "address", + value: { + string: "2120 Mifflin Rd.", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "sentences", + }, + }, + { + title: "date of birth", + id: "birthdate", + value: { + date: 252504060, + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "sex", + id: "sex", + value: { + gender: "male", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "height", + id: "height", + value: { + string: "5'11\"", + }, + indexAtSource: 4, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "number", + id: "number", + value: { + string: "12345678901", + }, + indexAtSource: 5, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "license class", + id: "class", + value: { + string: "C", + }, + indexAtSource: 6, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "conditions / restrictions", + id: "conditions", + value: { + string: "B", + }, + indexAtSource: 7, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "state", + id: "state", + value: { + string: "Pennsylvania", + }, + indexAtSource: 8, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "country", + id: "country", + value: { + string: "United States", + }, + indexAtSource: 9, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "expiry date", + id: "expiry_date", + value: { + monthYear: 203012, + }, + indexAtSource: 10, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + ], + }, + ], + passwordHistory: [], + }, + overview: { + subtitle: "12345678901", + title: "Michael Scarn", + url: "", + ps: 0, + pbe: 0.0, + pgrng: false, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts b/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts new file mode 100644 index 00000000..1f83a6d8 --- /dev/null +++ b/jslib/common/spec/importers/testData/onePassword1Pux/EmailAccount.ts @@ -0,0 +1,341 @@ +import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes"; + +export const EmailAccountData: ExportData = { + accounts: [ + { + attrs: { + accountName: "1Password Customer", + name: "1Password Customer", + avatar: "", + email: "username123123123@gmail.com", + uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E", + domain: "https://my.1password.com/", + }, + vaults: [ + { + attrs: { + uuid: "pqcgbqjxr4tng2hsqt5ffrgwju", + desc: "Just test entries", + avatar: "ke7i5rxnjrh3tj6uesstcosspu.png", + name: "T's Test Vault", + type: "U", + }, + items: [ + { + uuid: "p3hohdgwpt4u2ra2fc3tvzomsm", + favIndex: 0, + createdAt: 1619466428, + updatedAt: 1619466585, + trashed: false, + categoryUuid: "111", + details: { + loginFields: [], + notesPlain: "My Email Config", + sections: [ + { + title: "", + fields: [ + { + title: "type", + id: "pop_type", + value: { + menu: "either", + }, + indexAtSource: 0, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "default", + capitalization: "default", + }, + }, + { + title: "username", + id: "pop_username", + value: { + string: "someuser@nullvalue.test", + }, + indexAtSource: 1, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "default", + correction: "no", + capitalization: "none", + }, + }, + { + title: "server", + id: "pop_server", + value: { + string: "mailserver.nullvalue.test", + }, + indexAtSource: 2, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "uRL", + correction: "default", + capitalization: "default", + }, + }, + { + title: "port number", + id: "pop_port", + value: { + string: "587", + }, + indexAtSource: 3, + guarded: false, + multiline: false, + dontGenerate: false, + inputTraits: { + keyboard: "numberPad", + correction: "default", + capitalization: "default", + }, + }, + { + title: "password", + id: "pop_password", + value: { + concealed: "u1jsf + + + + + + + + + + + + This is a sample note. + 3 + + + + 5555123456789000 + John Smith + 01/23 + 555 + 1111 + 555-0153 + 1 + + + john555@gmail.com + early91*Fail* + https://www.facebook.com + 3 + + + john555@gmail.com + plain79{Area{ + https://www.google.com + 3 + thisisanotp + thisshouldbehidden + + + 555111111 + John Smith + 05/05/1980 + 01/01/2018 + 01/01/2028 + 3 + This is a note attached to a card + + + john555@gmail.com + https://twitter.com + 3 + shouldbepassword + + + john555 + Save63\apple\ + 3 + + +`; diff --git a/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts new file mode 100644 index 00000000..2f4fe6c3 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts @@ -0,0 +1,114 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { ApiLogInStrategy } from "jslib-common/misc/logInStrategies/apiLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +describe("ApiLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let environmentService: SubstituteOf; + let keyConnectorService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + + let apiLogInStrategy: ApiLogInStrategy; + let credentials: ApiLogInCredentials; + + const deviceId = Utils.newGuid(); + const keyConnectorUrl = "KEY_CONNECTOR_URL"; + const apiClientId = "API_CLIENT_ID"; + const apiClientSecret = "API_CLIENT_SECRET"; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + environmentService = Substitute.for(); + stateService = Substitute.for(); + keyConnectorService = Substitute.for(); + twoFactorService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + tokenService.getTwoFactorToken().resolves(null); + + apiLogInStrategy = new ApiLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + environmentService, + keyConnectorService + ); + + credentials = new ApiLogInCredentials(apiClientId, apiClientSecret); + }); + + it("sends api key credentials to the server", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + await apiLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const apiTokenRequest = actual as any; + return ( + apiTokenRequest.clientId === apiClientId && + apiTokenRequest.clientSecret === apiClientSecret && + apiTokenRequest.device.identifier === deviceId && + apiTokenRequest.twoFactor.provider == null && + apiTokenRequest.twoFactor.token == null && + apiTokenRequest.captchaResponse == null + ); + }) + ); + }); + + it("sets the local environment after a successful login", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await apiLogInStrategy.logIn(credentials); + + stateService.received(1).setApiKeyClientId(apiClientId); + stateService.received(1).setApiKeyClientSecret(apiClientSecret); + stateService.received(1).addAccount(Arg.any()); + }); + + it("gets and sets the Key Connector key from environmentUrl", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.apiUseKeyConnector = true; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + environmentService.getKeyConnectorUrl().returns(keyConnectorUrl); + + await apiLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts new file mode 100644 index 00000000..5f68881b --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/logIn.strategy.spec.ts @@ -0,0 +1,288 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; +import { PasswordLogInStrategy } from "jslib-common/misc/logInStrategies/passwordLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { Account, AccountProfile, AccountTokens } from "jslib-common/models/domain/account"; +import { AuthResult } from "jslib-common/models/domain/authResult"; +import { EncString } from "jslib-common/models/domain/encString"; +import { PasswordLogInCredentials } from "jslib-common/models/domain/logInCredentials"; +import { PasswordTokenRequest } from "jslib-common/models/request/identityToken/passwordTokenRequest"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; +import { IdentityCaptchaResponse } from "jslib-common/models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "jslib-common/models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "jslib-common/models/response/identityTwoFactorResponse"; + +const email = "hello@world.com"; +const masterPassword = "password"; + +const deviceId = Utils.newGuid(); +const accessToken = "ACCESS_TOKEN"; +const refreshToken = "REFRESH_TOKEN"; +const encKey = "ENC_KEY"; +const privateKey = "PRIVATE_KEY"; +const captchaSiteKey = "CAPTCHA_SITE_KEY"; +const kdf = 0; +const kdfIterations = 10000; +const userId = Utils.newGuid(); +const masterPasswordHash = "MASTER_PASSWORD_HASH"; + +const decodedToken = { + sub: userId, + email: email, + premium: false, +}; + +const twoFactorProviderType = TwoFactorProviderType.Authenticator; +const twoFactorToken = "TWO_FACTOR_TOKEN"; +const twoFactorRemember = true; + +export function identityTokenResponseFactory() { + return new IdentityTokenResponse({ + ForcePasswordReset: false, + Kdf: kdf, + KdfIterations: kdfIterations, + Key: encKey, + PrivateKey: privateKey, + ResetMasterPassword: false, + access_token: accessToken, + expires_in: 3600, + refresh_token: refreshToken, + scope: "api offline_access", + token_type: "Bearer", + }); +} + +describe("LogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + let authService: SubstituteOf; + + let passwordLogInStrategy: PasswordLogInStrategy; + let credentials: PasswordLogInCredentials; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + twoFactorService = Substitute.for(); + authService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + + // The base class is abstract so we test it via PasswordLogInStrategy + passwordLogInStrategy = new PasswordLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + authService + ); + credentials = new PasswordLogInCredentials(email, masterPassword); + }); + + describe("base class", () => { + it("sets the local environment after a successful login", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + tokenService.decodeToken(accessToken).resolves(decodedToken); + + await passwordLogInStrategy.logIn(credentials); + + stateService.received(1).addAccount( + new Account({ + profile: { + ...new AccountProfile(), + ...{ + userId: userId, + email: email, + hasPremiumPersonally: false, + kdfIterations: kdfIterations, + kdfType: kdf, + }, + }, + tokens: { + ...new AccountTokens(), + ...{ + accessToken: accessToken, + refreshToken: refreshToken, + }, + }, + }) + ); + cryptoService.received(1).setEncKey(encKey); + cryptoService.received(1).setEncPrivateKey(privateKey); + + stateService.received(1).setBiometricLocked(false); + messagingService.received(1).send("loggedIn"); + }); + + it("builds AuthResult", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.forcePasswordReset = true; + tokenResponse.resetMasterPassword = true; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + const expected = new AuthResult(); + expected.forcePasswordReset = true; + expected.resetMasterPassword = true; + expected.twoFactorProviders = null; + expected.captchaSiteKey = ""; + expect(result).toEqual(expected); + }); + + it("rejects login if CAPTCHA is required", async () => { + // Sample CAPTCHA response + const tokenResponse = new IdentityCaptchaResponse({ + error: "invalid_grant", + error_description: "Captcha required.", + HCaptcha_SiteKey: captchaSiteKey, + }); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + stateService.didNotReceive().addAccount(Arg.any()); + messagingService.didNotReceive().send(Arg.any()); + + const expected = new AuthResult(); + expected.captchaSiteKey = captchaSiteKey; + expect(result).toEqual(expected); + }); + + it("makes a new public and private key for an old account", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.privateKey = null; + cryptoService.makeKeyPair(Arg.any()).resolves(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postAccountKeys(Arg.any()); + }); + }); + + describe("Two-factor authentication", () => { + it("rejects login if 2FA is required", async () => { + // Sample response where TOTP 2FA required + const tokenResponse = new IdentityTwoFactorResponse({ + TwoFactorProviders: ["0"], + TwoFactorProviders2: { 0: null }, + error: "invalid_grant", + error_description: "Two factor required.", + }); + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const result = await passwordLogInStrategy.logIn(credentials); + + stateService.didNotReceive().addAccount(Arg.any()); + messagingService.didNotReceive().send(Arg.any()); + + const expected = new AuthResult(); + expected.twoFactorProviders = new Map(); + expected.twoFactorProviders.set(0, null); + expect(result).toEqual(expected); + }); + + it("sends stored 2FA token to server", async () => { + tokenService.getTwoFactorToken().resolves(twoFactorToken); + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === TwoFactorProviderType.Remember && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === false + ); + }) + ); + }); + + it("sends 2FA token provided by user to server (single step)", async () => { + // This occurs if the user enters the 2FA code as an argument in the CLI + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + credentials.twoFactor = new TokenRequestTwoFactor( + twoFactorProviderType, + twoFactorToken, + twoFactorRemember + ); + + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === twoFactorProviderType && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === twoFactorRemember + ); + }) + ); + }); + + it("sends 2FA token provided by user to server (two-step)", async () => { + // Simulate a partially completed login + passwordLogInStrategy.tokenRequest = new PasswordTokenRequest( + email, + masterPasswordHash, + null, + null + ); + + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await passwordLogInStrategy.logInTwoFactor( + new TokenRequestTwoFactor(twoFactorProviderType, twoFactorToken, twoFactorRemember), + null + ); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; + return ( + passwordTokenRequest.twoFactor.provider === twoFactorProviderType && + passwordTokenRequest.twoFactor.token === twoFactorToken && + passwordTokenRequest.twoFactor.remember === twoFactorRemember + ); + }) + ); + }); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts new file mode 100644 index 00000000..65bf5367 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts @@ -0,0 +1,110 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { HashPurpose } from "jslib-common/enums/hashPurpose"; +import { PasswordLogInStrategy } from "jslib-common/misc/logInStrategies/passwordLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { PasswordLogInCredentials } from "jslib-common/models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +const email = "hello@world.com"; +const masterPassword = "password"; +const hashedPassword = "HASHED_PASSWORD"; +const localHashedPassword = "LOCAL_HASHED_PASSWORD"; +const preloginKey = new SymmetricCryptoKey( + Utils.fromB64ToArray( + "N2KWjlLpfi5uHjv+YcfUKIpZ1l+W+6HRensmIqD+BFYBf6N/dvFpJfWwYnVBdgFCK2tJTAIMLhqzIQQEUmGFgg==" + ) +); +const deviceId = Utils.newGuid(); + +describe("PasswordLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + let authService: SubstituteOf; + + let passwordLogInStrategy: PasswordLogInStrategy; + let credentials: PasswordLogInCredentials; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + twoFactorService = Substitute.for(); + authService = Substitute.for(); + + appIdService.getAppId().resolves(deviceId); + tokenService.getTwoFactorToken().resolves(null); + + authService.makePreloginKey(Arg.any(), Arg.any()).resolves(preloginKey); + + cryptoService.hashPassword(masterPassword, Arg.any()).resolves(hashedPassword); + cryptoService + .hashPassword(masterPassword, Arg.any(), HashPurpose.LocalAuthorization) + .resolves(localHashedPassword); + + passwordLogInStrategy = new PasswordLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + authService + ); + credentials = new PasswordLogInCredentials(email, masterPassword); + + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + }); + + it("sends master password credentials to the server", async () => { + await passwordLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const passwordTokenRequest = actual as any; // Need to access private fields + return ( + passwordTokenRequest.email === email && + passwordTokenRequest.masterPasswordHash === hashedPassword && + passwordTokenRequest.device.identifier === deviceId && + passwordTokenRequest.twoFactor.provider == null && + passwordTokenRequest.twoFactor.token == null && + passwordTokenRequest.captchaResponse == null + ); + }) + ); + }); + + it("sets the local environment after a successful login", async () => { + await passwordLogInStrategy.logIn(credentials); + + cryptoService.received(1).setKey(preloginKey); + cryptoService.received(1).setKeyHash(localHashedPassword); + }); +}); diff --git a/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts b/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts new file mode 100644 index 00000000..826a16a5 --- /dev/null +++ b/jslib/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts @@ -0,0 +1,127 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { SsoLogInStrategy } from "jslib-common/misc/logInStrategies/ssoLogin.strategy"; +import { Utils } from "jslib-common/misc/utils"; +import { SsoLogInCredentials } from "jslib-common/models/domain/logInCredentials"; + +import { identityTokenResponseFactory } from "./logIn.strategy.spec"; + +describe("SsoLogInStrategy", () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let logService: SubstituteOf; + let keyConnectorService: SubstituteOf; + let stateService: SubstituteOf; + let twoFactorService: SubstituteOf; + + let ssoLogInStrategy: SsoLogInStrategy; + let credentials: SsoLogInCredentials; + + const deviceId = Utils.newGuid(); + const encKey = "ENC_KEY"; + const privateKey = "PRIVATE_KEY"; + const keyConnectorUrl = "KEY_CONNECTOR_URL"; + + const ssoCode = "SSO_CODE"; + const ssoCodeVerifier = "SSO_CODE_VERIFIER"; + const ssoRedirectUrl = "SSO_REDIRECT_URL"; + const ssoOrgId = "SSO_ORG_ID"; + + beforeEach(async () => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + logService = Substitute.for(); + stateService = Substitute.for(); + keyConnectorService = Substitute.for(); + twoFactorService = Substitute.for(); + + tokenService.getTwoFactorToken().resolves(null); + appIdService.getAppId().resolves(deviceId); + + ssoLogInStrategy = new SsoLogInStrategy( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService, + keyConnectorService + ); + credentials = new SsoLogInCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); + }); + + it("sends SSO information to server", async () => { + apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); + + await ssoLogInStrategy.logIn(credentials); + + apiService.received(1).postIdentityToken( + Arg.is((actual) => { + const ssoTokenRequest = actual as any; + return ( + ssoTokenRequest.code === ssoCode && + ssoTokenRequest.codeVerifier === ssoCodeVerifier && + ssoTokenRequest.redirectUri === ssoRedirectUrl && + ssoTokenRequest.device.identifier === deviceId && + ssoTokenRequest.twoFactor.provider == null && + ssoTokenRequest.twoFactor.token == null + ); + }) + ); + }); + + it("does not set keys for new SSO user flow", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.key = null; + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + cryptoService.didNotReceive().setEncPrivateKey(privateKey); + cryptoService.didNotReceive().setEncKey(encKey); + }); + + it("gets and sets KeyConnector key for enrolled user", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.keyConnectorUrl = keyConnectorUrl; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); + }); + + it("converts new SSO user to Key Connector on first login", async () => { + const tokenResponse = identityTokenResponseFactory(); + tokenResponse.keyConnectorUrl = keyConnectorUrl; + tokenResponse.key = null; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + keyConnectorService.received(1).convertNewSsoUserToKeyConnector(tokenResponse, ssoOrgId); + }); +}); diff --git a/jslib/common/spec/misc/sequentialize.spec.ts b/jslib/common/spec/misc/sequentialize.spec.ts new file mode 100644 index 00000000..4165eca2 --- /dev/null +++ b/jslib/common/spec/misc/sequentialize.spec.ts @@ -0,0 +1,127 @@ +import { sequentialize } from "jslib-common/misc/sequentialize"; + +describe("sequentialize decorator", () => { + it("should call the function once", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + }); + + it("should call the function once for each instance of the object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + promises.push(foo2.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + expect(foo2.calls).toBe(1); + }); + + it("should call the function once with key function", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(1); + }); + + it("should call the function again when already resolved", async () => { + const foo = new Foo(); + await foo.bar(1); + expect(foo.calls).toBe(1); + await foo.bar(1); + expect(foo.calls).toBe(2); + }); + + it("should call the function again when already resolved with a key function", async () => { + const foo = new Foo(); + await foo.baz(1); + expect(foo.calls).toBe(1); + await foo.baz(1); + expect(foo.calls).toBe(2); + }); + + it("should call the function for each argument", async () => { + const foo = new Foo(); + await Promise.all([foo.bar(1), foo.bar(1), foo.bar(2), foo.bar(2), foo.bar(3), foo.bar(3)]); + expect(foo.calls).toBe(3); + }); + + it("should call the function for each argument with key function", async () => { + const foo = new Foo(); + await Promise.all([foo.baz(1), foo.baz(1), foo.baz(2), foo.baz(2), foo.baz(3), foo.baz(3)]); + expect(foo.calls).toBe(3); + }); + + it("should return correct result for each call", async () => { + const foo = new Foo(); + const allRes: number[] = []; + + await Promise.all([ + foo.bar(1).then((res) => allRes.push(res)), + foo.bar(1).then((res) => allRes.push(res)), + foo.bar(2).then((res) => allRes.push(res)), + foo.bar(2).then((res) => allRes.push(res)), + foo.bar(3).then((res) => allRes.push(res)), + foo.bar(3).then((res) => allRes.push(res)), + ]); + expect(foo.calls).toBe(3); + expect(allRes.length).toBe(6); + allRes.sort(); + expect(allRes).toEqual([2, 2, 4, 4, 6, 6]); + }); + + it("should return correct result for each call with key function", async () => { + const foo = new Foo(); + const allRes: number[] = []; + + await Promise.all([ + foo.baz(1).then((res) => allRes.push(res)), + foo.baz(1).then((res) => allRes.push(res)), + foo.baz(2).then((res) => allRes.push(res)), + foo.baz(2).then((res) => allRes.push(res)), + foo.baz(3).then((res) => allRes.push(res)), + foo.baz(3).then((res) => allRes.push(res)), + ]); + expect(foo.calls).toBe(3); + expect(allRes.length).toBe(6); + allRes.sort(); + expect(allRes).toEqual([3, 3, 6, 6, 9, 9]); + }); +}); + +class Foo { + calls = 0; + + @sequentialize((args) => "bar" + args[0]) + bar(a: number): Promise { + this.calls++; + return new Promise((res) => { + setTimeout(() => { + res(a * 2); + }, Math.random() * 100); + }); + } + + @sequentialize((args) => "baz" + args[0]) + baz(a: number): Promise { + this.calls++; + return new Promise((res) => { + setTimeout(() => { + res(a * 3); + }, Math.random() * 100); + }); + } +} diff --git a/jslib/common/spec/misc/throttle.spec.ts b/jslib/common/spec/misc/throttle.spec.ts new file mode 100644 index 00000000..5a8cd27e --- /dev/null +++ b/jslib/common/spec/misc/throttle.spec.ts @@ -0,0 +1,110 @@ +import { sequentialize } from "jslib-common/misc/sequentialize"; +import { throttle } from "jslib-common/misc/throttle"; + +describe("throttle decorator", () => { + it("should call the function once at a time", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + }); + + it("should call the function once at a time for each object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.bar(1)); + promises.push(foo2.bar(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + expect(foo2.calls).toBe(10); + }); + + it("should call the function limit at a time", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + }); + + it("should call the function limit at a time for each object", async () => { + const foo = new Foo(); + const foo2 = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.baz(1)); + promises.push(foo2.baz(1)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(10); + expect(foo2.calls).toBe(10); + }); + + it("should work together with sequentialize", async () => { + const foo = new Foo(); + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(foo.qux(Math.floor(i / 2) * 2)); + } + await Promise.all(promises); + + expect(foo.calls).toBe(5); + }); +}); + +class Foo { + calls = 0; + inflight = 0; + + @throttle(1, () => "bar") + bar(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBe(1); + this.inflight--; + res(a * 2); + }, Math.random() * 10); + }); + } + + @throttle(5, () => "baz") + baz(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBeLessThanOrEqual(5); + this.inflight--; + res(a * 3); + }, Math.random() * 10); + }); + } + + @sequentialize((args) => "qux" + args[0]) + @throttle(1, () => "qux") + qux(a: number) { + this.calls++; + this.inflight++; + return new Promise((res) => { + setTimeout(() => { + expect(this.inflight).toBe(1); + this.inflight--; + res(a * 3); + }, Math.random() * 10); + }); + } +} diff --git a/jslib/common/spec/misc/utils.spec.ts b/jslib/common/spec/misc/utils.spec.ts new file mode 100644 index 00000000..eec41e4b --- /dev/null +++ b/jslib/common/spec/misc/utils.spec.ts @@ -0,0 +1,73 @@ +import { Utils } from "jslib-common/misc/utils"; + +describe("Utils Service", () => { + describe("getDomain", () => { + it("should fail for invalid urls", () => { + expect(Utils.getDomain(null)).toBeNull(); + expect(Utils.getDomain(undefined)).toBeNull(); + expect(Utils.getDomain(" ")).toBeNull(); + expect(Utils.getDomain('https://bit!:"_&ward.com')).toBeNull(); + expect(Utils.getDomain("bitwarden")).toBeNull(); + }); + + it("should fail for data urls", () => { + expect(Utils.getDomain("data:image/jpeg;base64,AAA")).toBeNull(); + }); + + it("should handle urls without protocol", () => { + expect(Utils.getDomain("bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("wrong://bitwarden.com")).toBe("bitwarden.com"); + }); + + it("should handle valid urls", () => { + expect(Utils.getDomain("https://bitwarden")).toBe("bitwarden"); + expect(Utils.getDomain("https://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("http://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getDomain("http://vault.bitwarden.com")).toBe("bitwarden.com"); + expect( + Utils.getDomain("https://user:password@bitwarden.com:8080/password/sites?and&query#hash") + ).toBe("bitwarden.com"); + expect(Utils.getDomain("https://bitwarden.unknown")).toBe("bitwarden.unknown"); + }); + + it("should support localhost and IP", () => { + expect(Utils.getDomain("https://localhost")).toBe("localhost"); + expect(Utils.getDomain("https://192.168.1.1")).toBe("192.168.1.1"); + }); + + it("should reject invalid hostnames", () => { + expect(Utils.getDomain("https://mywebsite.com$.mywebsite.com")).toBeNull(); + expect(Utils.getDomain("https://mywebsite.com!.mywebsite.com")).toBeNull(); + }); + }); + + describe("getHostname", () => { + it("should fail for invalid urls", () => { + expect(Utils.getHostname(null)).toBeNull(); + expect(Utils.getHostname(undefined)).toBeNull(); + expect(Utils.getHostname(" ")).toBeNull(); + expect(Utils.getHostname('https://bit!:"_&ward.com')).toBeNull(); + expect(Utils.getHostname("bitwarden")).toBeNull(); + }); + + it("should handle valid urls", () => { + expect(Utils.getHostname("bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("https://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("http://bitwarden.com")).toBe("bitwarden.com"); + expect(Utils.getHostname("http://vault.bitwarden.com")).toBe("vault.bitwarden.com"); + }); + + it("should support localhost and IP", () => { + expect(Utils.getHostname("https://localhost")).toBe("localhost"); + expect(Utils.getHostname("https://192.168.1.1")).toBe("192.168.1.1"); + }); + }); + + describe("newGuid", () => { + it("should create a valid guid", () => { + const validGuid = + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + expect(Utils.newGuid()).toMatch(validGuid); + }); + }); +}); diff --git a/jslib/common/spec/services/cipher.service.spec.ts b/jslib/common/spec/services/cipher.service.spec.ts new file mode 100644 index 00000000..79b75787 --- /dev/null +++ b/jslib/common/spec/services/cipher.service.spec.ts @@ -0,0 +1,69 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { FileUploadService } from "jslib-common/abstractions/fileUpload.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { SearchService } from "jslib-common/abstractions/search.service"; +import { SettingsService } from "jslib-common/abstractions/settings.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { Utils } from "jslib-common/misc/utils"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { EncArrayBuffer } from "jslib-common/models/domain/encArrayBuffer"; +import { EncString } from "jslib-common/models/domain/encString"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { CipherService } from "jslib-common/services/cipher.service"; + +const ENCRYPTED_TEXT = "This data has been encrypted"; +const ENCRYPTED_BYTES = new EncArrayBuffer(Utils.fromUtf8ToArray(ENCRYPTED_TEXT).buffer); + +describe("Cipher Service", () => { + let cryptoService: SubstituteOf; + let stateService: SubstituteOf; + let settingsService: SubstituteOf; + let apiService: SubstituteOf; + let fileUploadService: SubstituteOf; + let i18nService: SubstituteOf; + let searchService: SubstituteOf; + let logService: SubstituteOf; + + let cipherService: CipherService; + + beforeEach(() => { + cryptoService = Substitute.for(); + stateService = Substitute.for(); + settingsService = Substitute.for(); + apiService = Substitute.for(); + fileUploadService = Substitute.for(); + i18nService = Substitute.for(); + searchService = Substitute.for(); + logService = Substitute.for(); + + cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES); + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT)); + + cipherService = new CipherService( + cryptoService, + settingsService, + apiService, + fileUploadService, + i18nService, + () => searchService, + logService, + stateService + ); + }); + + it("attachments upload encrypted file contents", async () => { + const fileName = "filename"; + const fileData = new Uint8Array(10).buffer; + cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32).buffer)); + + await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData); + + fileUploadService + .received(1) + .uploadCipherAttachment(Arg.any(), Arg.any(), new EncString(ENCRYPTED_TEXT), ENCRYPTED_BYTES); + }); +}); diff --git a/jslib/common/spec/services/consoleLog.service.spec.ts b/jslib/common/spec/services/consoleLog.service.spec.ts new file mode 100644 index 00000000..3f3059f7 --- /dev/null +++ b/jslib/common/spec/services/consoleLog.service.spec.ts @@ -0,0 +1,102 @@ +import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; + +const originalConsole = console; +let caughtMessage: any; + +declare let console: any; + +export function interceptConsole(interceptions: any): object { + console = { + log: function () { + // eslint-disable-next-line + interceptions.log = arguments; + }, + warn: function () { + // eslint-disable-next-line + interceptions.warn = arguments; + }, + error: function () { + // eslint-disable-next-line + interceptions.error = arguments; + }, + }; + return interceptions; +} + +export function restoreConsole() { + console = originalConsole; +} + +describe("ConsoleLogService", () => { + let logService: ConsoleLogService; + beforeEach(() => { + caughtMessage = {}; + interceptConsole(caughtMessage); + logService = new ConsoleLogService(true); + }); + + afterAll(() => { + restoreConsole(); + }); + + it("filters messages below the set threshold", () => { + logService = new ConsoleLogService(true, () => true); + logService.debug("debug"); + logService.info("info"); + logService.warning("warning"); + logService.error("error"); + + expect(caughtMessage).toEqual({}); + }); + it("only writes debug messages in dev mode", () => { + logService = new ConsoleLogService(false); + + logService.debug("debug message"); + expect(caughtMessage.log).toBeUndefined(); + }); + + it("writes debug/info messages to console.log", () => { + logService.debug("this is a debug message"); + expect(caughtMessage).toMatchObject({ + log: { "0": "this is a debug message" }, + }); + + logService.info("this is an info message"); + expect(caughtMessage).toMatchObject({ + log: { "0": "this is an info message" }, + }); + }); + it("writes warning messages to console.warn", () => { + logService.warning("this is a warning message"); + expect(caughtMessage).toMatchObject({ + warn: { 0: "this is a warning message" }, + }); + }); + it("writes error messages to console.error", () => { + logService.error("this is an error message"); + expect(caughtMessage).toMatchObject({ + error: { 0: "this is an error message" }, + }); + }); + + it("times with output to info", async () => { + logService.time(); + await new Promise((r) => setTimeout(r, 250)); + const duration = logService.timeEnd(); + expect(duration[0]).toBe(0); + expect(duration[1]).toBeGreaterThan(0); + expect(duration[1]).toBeLessThan(500 * 10e6); + + expect(caughtMessage).toEqual(expect.arrayContaining([])); + expect(caughtMessage.log.length).toBe(1); + expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/)); + }); + + it("filters time output", async () => { + logService = new ConsoleLogService(true, () => true); + logService.time(); + logService.timeEnd(); + + expect(caughtMessage).toEqual({}); + }); +}); diff --git a/jslib/common/spec/services/export.service.spec.ts b/jslib/common/spec/services/export.service.spec.ts new file mode 100644 index 00000000..c96d9f0a --- /dev/null +++ b/jslib/common/spec/services/export.service.spec.ts @@ -0,0 +1,209 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CipherService } from "jslib-common/abstractions/cipher.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { FolderService } from "jslib-common/abstractions/folder.service"; +import { CipherType } from "jslib-common/enums/cipherType"; +import { KdfType } from "jslib-common/enums/kdfType"; +import { Utils } from "jslib-common/misc/utils"; +import { Cipher } from "jslib-common/models/domain/cipher"; +import { EncString } from "jslib-common/models/domain/encString"; +import { Login } from "jslib-common/models/domain/login"; +import { CipherWithIds as CipherExport } from "jslib-common/models/export/cipherWithIds"; +import { CipherView } from "jslib-common/models/view/cipherView"; +import { LoginView } from "jslib-common/models/view/loginView"; +import { ExportService } from "jslib-common/services/export.service"; + +import { BuildTestObject, GetUniqueString } from "../utils"; + +const UserCipherViews = [ + generateCipherView(false), + generateCipherView(false), + generateCipherView(true), +]; + +const UserCipherDomains = [ + generateCipherDomain(false), + generateCipherDomain(false), + generateCipherDomain(true), +]; + +function generateCipherView(deleted: boolean) { + return BuildTestObject( + { + id: GetUniqueString("id"), + notes: GetUniqueString("notes"), + type: CipherType.Login, + login: BuildTestObject( + { + username: GetUniqueString("username"), + password: GetUniqueString("password"), + }, + LoginView + ), + collectionIds: null, + deletedDate: deleted ? new Date() : null, + }, + CipherView + ); +} + +function generateCipherDomain(deleted: boolean) { + return BuildTestObject( + { + id: GetUniqueString("id"), + notes: new EncString(GetUniqueString("notes")), + type: CipherType.Login, + login: BuildTestObject( + { + username: new EncString(GetUniqueString("username")), + password: new EncString(GetUniqueString("password")), + }, + Login + ), + collectionIds: null, + deletedDate: deleted ? new Date() : null, + }, + Cipher + ); +} + +function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string) { + const actual = JSON.stringify(JSON.parse(jsonResult).items); + const items: CipherExport[] = []; + ciphers.forEach((c: CipherView | Cipher) => { + const item = new CipherExport(); + item.build(c); + items.push(item); + }); + + expect(actual).toEqual(JSON.stringify(items)); +} + +describe("ExportService", () => { + let exportService: ExportService; + let apiService: SubstituteOf; + let cryptoFunctionService: SubstituteOf; + let cipherService: SubstituteOf; + let folderService: SubstituteOf; + let cryptoService: SubstituteOf; + + beforeEach(() => { + apiService = Substitute.for(); + cryptoFunctionService = Substitute.for(); + cipherService = Substitute.for(); + folderService = Substitute.for(); + cryptoService = Substitute.for(); + + folderService.getAllDecrypted().resolves([]); + folderService.getAll().resolves([]); + + exportService = new ExportService( + folderService, + cipherService, + apiService, + cryptoService, + cryptoFunctionService + ); + }); + + it("exports unecrypted user ciphers", async () => { + cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1)); + + const actual = await exportService.getExport("json"); + + expectEqualCiphers(UserCipherViews.slice(0, 1), actual); + }); + + it("exports encrypted json user ciphers", async () => { + cipherService.getAll().resolves(UserCipherDomains.slice(0, 1)); + + const actual = await exportService.getExport("encrypted_json"); + + expectEqualCiphers(UserCipherDomains.slice(0, 1), actual); + }); + + it("does not unecrypted export trashed user items", async () => { + cipherService.getAllDecrypted().resolves(UserCipherViews); + + const actual = await exportService.getExport("json"); + + expectEqualCiphers(UserCipherViews.slice(0, 2), actual); + }); + + it("does not encrypted export trashed user items", async () => { + cipherService.getAll().resolves(UserCipherDomains); + + const actual = await exportService.getExport("encrypted_json"); + + expectEqualCiphers(UserCipherDomains.slice(0, 2), actual); + }); + + describe("password protected export", () => { + let exportString: string; + let exportObject: any; + let mac: SubstituteOf; + let data: SubstituteOf; + const password = "password"; + const salt = "salt"; + + describe("export json object", () => { + beforeEach(async () => { + mac = Substitute.for(); + data = Substitute.for(); + + mac.encryptedString.returns("mac"); + data.encryptedString.returns("encData"); + + jest.spyOn(Utils, "fromBufferToB64").mockReturnValue(salt); + cipherService.getAllDecrypted().resolves(UserCipherViews.slice(0, 1)); + + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + }); + + it("specifies it is encrypted", () => { + expect(exportObject.encrypted).toBe(true); + }); + + it("specifies it's password protected", () => { + expect(exportObject.passwordProtected).toBe(true); + }); + + it("specifies salt", () => { + expect(exportObject.salt).toEqual("salt"); + }); + + it("specifies kdfIterations", () => { + expect(exportObject.kdfIterations).toEqual(100000); + }); + + it("has kdfType", () => { + expect(exportObject.kdfType).toEqual(KdfType.PBKDF2_SHA256); + }); + + it("has a mac property", async () => { + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(mac); + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + + expect(exportObject.encKeyValidation_DO_NOT_EDIT).toEqual(mac.encryptedString); + }); + + it("has data property", async () => { + cryptoService.encrypt(Arg.any(), Arg.any()).resolves(data); + exportString = await exportService.getPasswordProtectedExport(password); + exportObject = JSON.parse(exportString); + + expect(exportObject.data).toEqual(data.encryptedString); + }); + + it("encrypts the data property", async () => { + const unencrypted = await exportService.getExport(); + expect(exportObject.data).not.toEqual(unencrypted); + }); + }); + }); +}); diff --git a/jslib/common/spec/services/import.service.spec.ts b/jslib/common/spec/services/import.service.spec.ts new file mode 100644 index 00000000..dbf31cb2 --- /dev/null +++ b/jslib/common/spec/services/import.service.spec.ts @@ -0,0 +1,72 @@ +import Substitute, { SubstituteOf } from "@fluffy-spoon/substitute"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CipherService } from "jslib-common/abstractions/cipher.service"; +import { CollectionService } from "jslib-common/abstractions/collection.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { FolderService } from "jslib-common/abstractions/folder.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { BitwardenPasswordProtectedImporter } from "jslib-common/importers/bitwardenPasswordProtectedImporter"; +import { Importer } from "jslib-common/importers/importer"; +import { Utils } from "jslib-common/misc/utils"; +import { ImportService } from "jslib-common/services/import.service"; + +describe("ImportService", () => { + let importService: ImportService; + let cipherService: SubstituteOf; + let folderService: SubstituteOf; + let apiService: SubstituteOf; + let i18nService: SubstituteOf; + let collectionService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let cryptoService: SubstituteOf; + + beforeEach(() => { + cipherService = Substitute.for(); + folderService = Substitute.for(); + apiService = Substitute.for(); + i18nService = Substitute.for(); + collectionService = Substitute.for(); + platformUtilsService = Substitute.for(); + cryptoService = Substitute.for(); + + importService = new ImportService( + cipherService, + folderService, + apiService, + i18nService, + collectionService, + platformUtilsService, + cryptoService + ); + }); + + describe("getImporterInstance", () => { + describe("Get bitPasswordProtected importer", () => { + let importer: Importer; + const organizationId = Utils.newGuid(); + const password = Utils.newGuid(); + + beforeEach(() => { + importer = importService.getImporter( + "bitwardenpasswordprotected", + organizationId, + password + ); + }); + + it("returns an instance of BitwardenPasswordProtectedImporter", () => { + expect(importer).toBeInstanceOf(BitwardenPasswordProtectedImporter); + }); + + it("has the appropriate organization Id", () => { + expect(importer.organizationId).toEqual(organizationId); + }); + + it("has the appropriate password", () => { + expect(Object.entries(importer)).toEqual(expect.arrayContaining([["password", password]])); + }); + }); + }); +}); diff --git a/jslib/common/spec/services/stateMigration.service.ts b/jslib/common/spec/services/stateMigration.service.ts new file mode 100644 index 00000000..d325585a --- /dev/null +++ b/jslib/common/spec/services/stateMigration.service.ts @@ -0,0 +1,84 @@ +import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StateVersion } from "jslib-common/enums/stateVersion"; +import { StateFactory } from "jslib-common/factories/stateFactory"; +import { Account } from "jslib-common/models/domain/account"; +import { GlobalState } from "jslib-common/models/domain/globalState"; +import { StateMigrationService } from "jslib-common/services/stateMigration.service"; + +const userId = "USER_ID"; + +describe("State Migration Service", () => { + let storageService: SubstituteOf; + let secureStorageService: SubstituteOf; + let stateFactory: SubstituteOf; + + let stateMigrationService: StateMigrationService; + + beforeEach(() => { + storageService = Substitute.for(); + secureStorageService = Substitute.for(); + stateFactory = Substitute.for(); + + stateMigrationService = new StateMigrationService( + storageService, + secureStorageService, + stateFactory + ); + }); + + describe("StateVersion 3 to 4 migration", async () => { + beforeEach(() => { + const globalVersion3: Partial = { + stateVersion: StateVersion.Three, + }; + + storageService.get("global", Arg.any()).resolves(globalVersion3); + storageService.get("authenticatedAccounts", Arg.any()).resolves([userId]); + }); + + it("clears everBeenUnlocked", async () => { + const accountVersion3: Account = { + profile: { + apiKeyClientId: null, + convertAccountToKeyConnector: null, + email: "EMAIL", + emailVerified: true, + everBeenUnlocked: true, + hasPremiumPersonally: false, + kdfIterations: 100000, + kdfType: 0, + keyHash: "KEY_HASH", + lastSync: "LAST_SYNC", + userId: userId, + usesKeyConnector: false, + forcePasswordReset: false, + }, + }; + + const expectedAccountVersion4: Account = { + profile: { + ...accountVersion3.profile, + }, + }; + delete expectedAccountVersion4.profile.everBeenUnlocked; + + storageService.get(userId, Arg.any()).resolves(accountVersion3); + + await stateMigrationService.migrate(); + + storageService.received(1).save(userId, expectedAccountVersion4, Arg.any()); + }); + + it("updates StateVersion number", async () => { + await stateMigrationService.migrate(); + + storageService.received(1).save( + "global", + Arg.is((globals: GlobalState) => globals.stateVersion === StateVersion.Four), + Arg.any() + ); + }); + }); +}); diff --git a/jslib/common/spec/test.ts b/jslib/common/spec/test.ts new file mode 100644 index 00000000..9d9906aa --- /dev/null +++ b/jslib/common/spec/test.ts @@ -0,0 +1,5 @@ +import { webcrypto } from "crypto"; + +Object.defineProperty(window, "crypto", { + value: webcrypto, +}); diff --git a/jslib/common/spec/utils.ts b/jslib/common/spec/utils.ts new file mode 100644 index 00000000..58963d0f --- /dev/null +++ b/jslib/common/spec/utils.ts @@ -0,0 +1,37 @@ +import Substitute, { Arg } from "@fluffy-spoon/substitute"; + +import { EncString } from "jslib-common/models/domain/encString"; + +function newGuid() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} + +export function GetUniqueString(prefix = "") { + return prefix + "_" + newGuid(); +} + +export function BuildTestObject( + def: Partial> | T, + constructor?: new () => T +): T { + return Object.assign(constructor === null ? {} : new constructor(), def) as T; +} + +export function mockEnc(s: string): EncString { + const mock = Substitute.for(); + mock.decrypt(Arg.any(), Arg.any()).resolves(s); + + return mock; +} + +export function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts new file mode 100644 index 00000000..3e437c6a --- /dev/null +++ b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts @@ -0,0 +1,558 @@ +import Substitute from "@fluffy-spoon/substitute"; + +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { Utils } from "jslib-common/misc/utils"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; + +const RsaPublicKey = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + + "RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN" + + "084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc" + + "xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB"; +const RsaPrivateKey = + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz" + + "YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L" + + "nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/" + + "YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK" + + "PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q" + + "Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj" + + "WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh" + + "5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk" + + "1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU" + + "BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf" + + "TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU" + + "q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv" + + "q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX" + + "5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1" + + "eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE" + + "Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8" + + "+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ" + + "BokBGnjFnTnKcs7nv/O8="; + +const Sha1Mac = "4d4c223f95dc577b665ec4ccbcb680b80a397038"; +const Sha256Mac = "6be3caa84922e12aaaaa2f16c40d44433bb081ef323db584eb616333ab4e874f"; +const Sha512Mac = + "21910e341fa12106ca35758a2285374509326c9fbe0bd64e7b99c898f841dc948c58ce66d3504d8883c" + + "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; + +describe("WebCrypto Function Service", () => { + describe("pbkdf2", () => { + const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; + const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; + const unicode256Key = "ZdeOata6xoRpB4DLp8zHhXz5kLmkWtX5pd+TdRH8w8w="; + + const regular512Key = + "liTi/Ke8LPU1Qv+Vl7NGEVt/XMbsBVJ2kQxtVG/Z1/JFHFKQW3ZkI81qVlwTiCpb+cFXzs+57" + + "eyhhx5wfKo5Cg=="; + const utf8512Key = + "df0KdvIBeCzD/kyXptwQohaqUa4e7IyFUyhFQjXCANu5T+scq55hCcE4dG4T/MhAk2exw8j7ixRN" + + "zXANiVZpnw=="; + const unicode512Key = + "FE+AnUJaxv8jh+zUDtZz4mjjcYk0/PZDZm+SLJe3XtxtnpdqqpblX6JjuMZt/dYYNMOrb2+mD" + + "L3FiQDTROh1lg=="; + + testPbkdf2("sha256", regular256Key, utf8256Key, unicode256Key); + testPbkdf2("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdf", () => { + const regular256Key = "qBUmEYtwTwwGPuw/z6bs/qYXXYNUlocFlyAuuANI8Pw="; + const utf8256Key = "6DfJwW1R3txgiZKkIFTvVAb7qVlG7lKcmJGJoxR2GBU="; + const unicode256Key = "gejGI82xthA+nKtKmIh82kjw+ttHr+ODsUoGdu5sf0A="; + + const regular512Key = "xe5cIG6ZfwGmb1FvsOedM0XKOm21myZkjL/eDeKIqqM="; + const utf8512Key = "XQMVBnxVEhlvjSFDQc77j5GDE9aorvbS0vKnjhRg0LY="; + const unicode512Key = "148GImrTbrjaGAe/iWEpclINM8Ehhko+9lB14+52lqc="; + + testHkdf("sha256", regular256Key, utf8256Key, unicode256Key); + testHkdf("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdfExpand", () => { + const prk16Byte = "criAmKtfzxanbgea5/kelQ=="; + const prk32Byte = "F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y="; + const prk64Byte = + "ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+" + + "gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=="; + + testHkdfExpand("sha256", prk32Byte, 32, "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8="); + testHkdfExpand( + "sha256", + prk32Byte, + 64, + "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+" + + "/queOZenPNkDhmlVyL2WZ3OSU5+7ISNF5NhNfvZA==" + ); + testHkdfExpand("sha512", prk64Byte, 32, "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlk="); + testHkdfExpand( + "sha512", + prk64Byte, + 64, + "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlkY5Pv0sB+" + + "MqvaopmkC6sD/j89zDwTV9Ib2fpucUydO8w==" + ); + + it("should fail with prk too small", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk16Byte), + "info", + 32, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("prk is too small.")); + }); + + it("should fail with outputByteSize is too large", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk32Byte), + "info", + 8161, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("outputByteSize is too large.")); + }); + }); + + describe("hash", () => { + const regular1Hash = "2a241604fb921fad12bf877282457268e1dccb70"; + const utf81Hash = "85672798dc5831e96d6c48655d3d39365a9c88b6"; + const unicode1Hash = "39c975935054a3efc805a9709b60763a823a6ad4"; + + const regular256Hash = "2b8e96031d352a8655d733d7a930b5ffbea69dc25cf65c7bca7dd946278908b2"; + const utf8256Hash = "25fe8440f5b01ed113b0a0e38e721b126d2f3f77a67518c4a04fcde4e33eeb9d"; + const unicode256Hash = "adc1c0c2afd6e92cefdf703f9b6eb2c38e0d6d1a040c83f8505c561fea58852e"; + + const regular512Hash = + "c15cf11d43bde333647e3f559ec4193bb2edeaa0e8b902772f514cdf3f785a3f49a6e02a4b87b3" + + "b47523271ad45b7e0aebb5cdcc1bc54815d256eb5dcb80da9d"; + const utf8512Hash = + "035c31a877a291af09ed2d3a1a293e69c3e079ea2cecc00211f35e6bce10474ca3ad6e30b59e26118" + + "37463f20969c5bc95282965a051a88f8cdf2e166549fcdd"; + const unicode512Hash = + "2b16a5561af8ad6fe414cc103fc8036492e1fc6d9aabe1b655497054f760fe0e34c5d100ac773d" + + "9f3030438284f22dbfa20cb2e9b019f2c98dfe38ce1ef41bae"; + + const regularMd5 = "5eceffa53a5fd58c44134211e2c5f522"; + const utf8Md5 = "3abc9433c09551b939c80aa0aa3174e1"; + const unicodeMd5 = "85ae134072c8d81257933f7045ba17ca"; + + testHash("sha1", regular1Hash, utf81Hash, unicode1Hash); + testHash("sha256", regular256Hash, utf8256Hash, unicode256Hash); + testHash("sha512", regular512Hash, utf8512Hash, unicode512Hash); + testHash("md5", regularMd5, utf8Md5, unicodeMd5); + }); + + describe("hmac", () => { + testHmac("sha1", Sha1Mac); + testHmac("sha256", Sha256Mac); + testHmac("sha512", Sha512Mac); + }); + + describe("compare", () => { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const equal = await cryptoFunctionService.compare(a.buffer, a.buffer); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + }); + + describe("hmacFast", () => { + testHmacFast("sha1", Sha1Mac); + testHmacFast("sha256", Sha256Mac); + testHmacFast("sha512", Sha512Mac); + }); + + describe("compareFast", () => { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, aByteString); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const bByteString = Utils.fromBufferToByteString(b.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const aByteString = Utils.fromBufferToByteString(a.buffer); + const b = new Uint8Array(2); + b[0] = 3; + const bByteString = Utils.fromBufferToByteString(b.buffer); + const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); + expect(equal).toBe(false); + }); + }); + + describe("aesEncrypt", () => { + it("should successfully encrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromUtf8ToArray("EncryptMe!"); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); + }); + + it("should successfully encrypt and then decrypt data fast", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const encData = Utils.fromBufferToB64(encValue); + const b64Iv = Utils.fromBufferToB64(iv.buffer); + const symKey = new SymmetricCryptoKey(key.buffer); + const params = cryptoFunctionService.aesDecryptFastParameters(encData, b64Iv, null, symKey); + const decValue = await cryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe(value); + }); + + it("should successfully encrypt and then decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("aesDecryptFast", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = "ByUF8vhyX4ddU9gcooznwA=="; + const params = cryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await cryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe("EncryptMe!"); + }); + }); + + describe("aesDecrypt", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); + const decValue = await cryptoFunctionService.aesDecrypt(data.buffer, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaEncrypt", () => { + it("should successfully encrypt and then decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await cryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, "sha1"); + const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("rsaDecrypt", () => { + it("should successfully decrypt data", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray( + "A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV" + + "4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT" + + "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" + ); + const decValue = await cryptoFunctionService.rsaDecrypt(data.buffer, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaExtractPublicKey", () => { + it("should successfully extract key", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + + describe("rsaGenerateKeyPair", () => { + testRsaGenerateKeyPair(1024); + testRsaGenerateKeyPair(2048); + + // Generating 4096 bit keys can be slow. Commenting it out to save CI. + // testRsaGenerateKeyPair(4096); + }); + + describe("randomBytes", () => { + it("should make a value of the correct length", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await cryptoFunctionService.randomBytes(16); + expect(randomData.byteLength).toBe(16); + }); + + it("should not make the same value twice", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const randomData = await cryptoFunctionService.randomBytes(16); + const randomData2 = await cryptoFunctionService.randomBytes(16); + expect( + randomData.byteLength === randomData2.byteLength && randomData !== randomData2 + ).toBeTruthy(); + }); + }); +}); + +function testPbkdf2( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const regularEmail = "user@example.com"; + const utf8Email = "üser@example.com"; + + const regularPassword = "password"; + const utf8Password = "pǻssword"; + const unicodePassword = "😀password🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(regularPassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(utf8Password, utf8Email, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(unicodePassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2( + Utils.fromUtf8ToArray(regularPassword).buffer, + Utils.fromUtf8ToArray(regularEmail).buffer, + algorithm, + 5000 + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdf( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const ikm = Utils.fromB64ToArray("criAmKtfzxanbgea5/kelQ=="); + + const regularSalt = "salt"; + const utf8Salt = "üser_salt"; + const unicodeSalt = "😀salt🙏"; + + const regularInfo = "info"; + const utf8Info = "üser_info"; + const unicodeInfo = "😀info🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, regularSalt, regularInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, utf8Salt, utf8Info, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, unicodeSalt, unicodeInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf( + ikm, + Utils.fromUtf8ToArray(regularSalt).buffer, + Utils.fromUtf8ToArray(regularInfo).buffer, + 32, + algorithm + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdfExpand( + algorithm: "sha256" | "sha512", + b64prk: string, + outputByteSize: number, + b64ExpectedOkm: string +) { + const info = "info"; + + it("should create valid " + algorithm + " " + outputByteSize + " byte okm", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const okm = await cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(b64prk), + info, + outputByteSize, + algorithm + ); + expect(Utils.fromBufferToB64(okm)).toBe(b64ExpectedOkm); + }); +} + +function testHash( + algorithm: "sha1" | "sha256" | "sha512" | "md5", + regularHash: string, + utf8Hash: string, + unicodeHash: string +) { + const regularValue = "HashMe!!"; + const utf8Value = "HǻshMe!!"; + const unicodeValue = "😀HashMe!!!🙏"; + + it("should create valid " + algorithm + " hash from regular input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(regularValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); + + it("should create valid " + algorithm + " hash from utf8 input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(utf8Value, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(utf8Hash); + }); + + it("should create valid " + algorithm + " hash from unicode input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(unicodeValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(unicodeHash); + }); + + it("should create valid " + algorithm + " hash from array buffer input", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const hash = await cryptoFunctionService.hash( + Utils.fromUtf8ToArray(regularValue).buffer, + algorithm + ); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); +} + +function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const computedMac = await cryptoFunctionService.hmac( + Utils.fromUtf8ToArray("SignMe!!").buffer, + Utils.fromUtf8ToArray("secretkey").buffer, + algorithm + ); + expect(Utils.fromBufferToHex(computedMac)).toBe(mac); + }); +} + +function testHmacFast(algorithm: "sha1" | "sha256" | "sha512", mac: string) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey").buffer); + const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!").buffer); + const computedMac = await cryptoFunctionService.hmacFast( + dataByteString, + keyByteString, + algorithm + ); + expect(Utils.fromBufferToHex(Utils.fromByteStringToArray(computedMac).buffer)).toBe(mac); + }); +} + +function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { + it( + "should successfully generate a " + length + " bit key pair", + async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const keyPair = await cryptoFunctionService.rsaGenerateKeyPair(length); + expect(keyPair[0] == null || keyPair[1] == null).toBe(false); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]); + expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey)); + }, + 30000 + ); +} + +function getWebCryptoFunctionService() { + const platformUtilsMock = Substitute.for(); + platformUtilsMock.isEdge().mimicks(() => navigator.userAgent.indexOf(" Edg/") !== -1); + + return new WebCryptoFunctionService(window); +} + +function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/common/src/abstractions/api.service.ts b/jslib/common/src/abstractions/api.service.ts new file mode 100644 index 00000000..c9970ce8 --- /dev/null +++ b/jslib/common/src/abstractions/api.service.ts @@ -0,0 +1,679 @@ +import { PolicyType } from "../enums/policyType"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkRestoreRequest } from "../models/request/cipherBulkRestoreRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { DeleteRecoverRequest } from "../models/request/deleteRecoverRequest"; +import { EmailRequest } from "../models/request/emailRequest"; +import { EmailTokenRequest } from "../models/request/emailTokenRequest"; +import { EmergencyAccessAcceptRequest } from "../models/request/emergencyAccessAcceptRequest"; +import { EmergencyAccessConfirmRequest } from "../models/request/emergencyAccessConfirmRequest"; +import { EmergencyAccessInviteRequest } from "../models/request/emergencyAccessInviteRequest"; +import { EmergencyAccessPasswordRequest } from "../models/request/emergencyAccessPasswordRequest"; +import { EmergencyAccessUpdateRequest } from "../models/request/emergencyAccessUpdateRequest"; +import { EventRequest } from "../models/request/eventRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { GroupRequest } from "../models/request/groupRequest"; +import { IapCheckRequest } from "../models/request/iapCheckRequest"; +import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KdfRequest } from "../models/request/kdfRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest"; +import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest"; +import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest"; +import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest"; +import { OrganizationImportRequest } from "../models/request/organizationImportRequest"; +import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest"; +import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest"; +import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest"; +import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest"; +import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest"; +import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest"; +import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest"; +import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest"; +import { OrganizationUserConfirmRequest } from "../models/request/organizationUserConfirmRequest"; +import { OrganizationUserInviteRequest } from "../models/request/organizationUserInviteRequest"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organizationUserResetPasswordEnrollmentRequest"; +import { OrganizationUserResetPasswordRequest } from "../models/request/organizationUserResetPasswordRequest"; +import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizationUserUpdateGroupsRequest"; +import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; +import { PasswordHintRequest } from "../models/request/passwordHintRequest"; +import { PasswordRequest } from "../models/request/passwordRequest"; +import { PaymentRequest } from "../models/request/paymentRequest"; +import { PolicyRequest } from "../models/request/policyRequest"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; +import { ProviderOrganizationCreateRequest } from "../models/request/provider/providerOrganizationCreateRequest"; +import { ProviderSetupRequest } from "../models/request/provider/providerSetupRequest"; +import { ProviderUpdateRequest } from "../models/request/provider/providerUpdateRequest"; +import { ProviderUserAcceptRequest } from "../models/request/provider/providerUserAcceptRequest"; +import { ProviderUserBulkConfirmRequest } from "../models/request/provider/providerUserBulkConfirmRequest"; +import { ProviderUserBulkRequest } from "../models/request/provider/providerUserBulkRequest"; +import { ProviderUserConfirmRequest } from "../models/request/provider/providerUserConfirmRequest"; +import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest"; +import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest"; +import { RegisterRequest } from "../models/request/registerRequest"; +import { SeatRequest } from "../models/request/seatRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest"; +import { SendAccessRequest } from "../models/request/sendAccessRequest"; +import { SendRequest } from "../models/request/sendRequest"; +import { SetPasswordRequest } from "../models/request/setPasswordRequest"; +import { StorageRequest } from "../models/request/storageRequest"; +import { TaxInfoUpdateRequest } from "../models/request/taxInfoUpdateRequest"; +import { TwoFactorEmailRequest } from "../models/request/twoFactorEmailRequest"; +import { TwoFactorProviderRequest } from "../models/request/twoFactorProviderRequest"; +import { TwoFactorRecoveryRequest } from "../models/request/twoFactorRecoveryRequest"; +import { UpdateDomainsRequest } from "../models/request/updateDomainsRequest"; +import { UpdateKeyRequest } from "../models/request/updateKeyRequest"; +import { UpdateProfileRequest } from "../models/request/updateProfileRequest"; +import { UpdateTempPasswordRequest } from "../models/request/updateTempPasswordRequest"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/updateTwoFactorAuthenticatorRequest"; +import { UpdateTwoFactorDuoRequest } from "../models/request/updateTwoFactorDuoRequest"; +import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEmailRequest"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest"; +import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest"; +import { VerifyBankRequest } from "../models/request/verifyBankRequest"; +import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest"; +import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; +import { ApiKeyResponse } from "../models/response/apiKeyResponse"; +import { AttachmentResponse } from "../models/response/attachmentResponse"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { BillingResponse } from "../models/response/billingResponse"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { + CollectionGroupDetailsResponse, + CollectionResponse, +} from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { + EmergencyAccessGranteeDetailsResponse, + EmergencyAccessGrantorDetailsResponse, + EmergencyAccessTakeoverResponse, + EmergencyAccessViewResponse, +} from "../models/response/emergencyAccessResponse"; +import { EventResponse } from "../models/response/eventResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { GroupDetailsResponse, GroupResponse } from "../models/response/groupResponse"; +import { IdentityCaptchaResponse } from "../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; +import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse"; +import { ListResponse } from "../models/response/listResponse"; +import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse"; +import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse"; +import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse"; +import { OrganizationResponse } from "../models/response/organizationResponse"; +import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse"; +import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse"; +import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse"; +import { + OrganizationUserDetailsResponse, + OrganizationUserResetPasswordDetailsReponse, + OrganizationUserUserDetailsResponse, +} from "../models/response/organizationUserResponse"; +import { PaymentResponse } from "../models/response/paymentResponse"; +import { PlanResponse } from "../models/response/planResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { PreloginResponse } from "../models/response/preloginResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { + ProviderOrganizationOrganizationDetailsResponse, + ProviderOrganizationResponse, +} from "../models/response/provider/providerOrganizationResponse"; +import { ProviderResponse } from "../models/response/provider/providerResponse"; +import { ProviderUserBulkPublicKeyResponse } from "../models/response/provider/providerUserBulkPublicKeyResponse"; +import { ProviderUserBulkResponse } from "../models/response/provider/providerUserBulkResponse"; +import { + ProviderUserResponse, + ProviderUserUserDetailsResponse, +} from "../models/response/provider/providerUserResponse"; +import { SelectionReadOnlyResponse } from "../models/response/selectionReadOnlyResponse"; +import { SendAccessResponse } from "../models/response/sendAccessResponse"; +import { SendFileDownloadDataResponse } from "../models/response/sendFileDownloadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SubscriptionResponse } from "../models/response/subscriptionResponse"; +import { SyncResponse } from "../models/response/syncResponse"; +import { TaxInfoResponse } from "../models/response/taxInfoResponse"; +import { TaxRateResponse } from "../models/response/taxRateResponse"; +import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse"; +import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse"; +import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse"; +import { TwoFactorProviderResponse } from "../models/response/twoFactorProviderResponse"; +import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse"; +import { + ChallengeResponse, + TwoFactorWebAuthnResponse, +} from "../models/response/twoFactorWebAuthnResponse"; +import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse"; +import { UserKeyResponse } from "../models/response/userKeyResponse"; +import { SendAccessView } from "../models/view/sendAccessView"; + +export abstract class ApiService { + postIdentityToken: ( + request: PasswordTokenRequest | SsoTokenRequest | ApiTokenRequest + ) => Promise; + refreshIdentityToken: () => Promise; + + getProfile: () => Promise; + getUserBilling: () => Promise; + getUserSubscription: () => Promise; + getTaxInfo: () => Promise; + putProfile: (request: UpdateProfileRequest) => Promise; + putTaxInfo: (request: TaxInfoUpdateRequest) => Promise; + postPrelogin: (request: PreloginRequest) => Promise; + postEmailToken: (request: EmailTokenRequest) => Promise; + postEmail: (request: EmailRequest) => Promise; + postPassword: (request: PasswordRequest) => Promise; + setPassword: (request: SetPasswordRequest) => Promise; + postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise; + postSecurityStamp: (request: SecretVerificationRequest) => Promise; + deleteAccount: (request: SecretVerificationRequest) => Promise; + getAccountRevisionDate: () => Promise; + postPasswordHint: (request: PasswordHintRequest) => Promise; + postRegister: (request: RegisterRequest) => Promise; + postPremium: (data: FormData) => Promise; + postIapCheck: (request: IapCheckRequest) => Promise; + postReinstatePremium: () => Promise; + postCancelPremium: () => Promise; + postAccountStorage: (request: StorageRequest) => Promise; + postAccountPayment: (request: PaymentRequest) => Promise; + postAccountLicense: (data: FormData) => Promise; + postAccountKey: (request: UpdateKeyRequest) => Promise; + postAccountKeys: (request: KeysRequest) => Promise; + postAccountVerifyEmail: () => Promise; + postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise; + postAccountVerifyPassword: (request: SecretVerificationRequest) => Promise; + postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise; + postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise; + postAccountKdf: (request: KdfRequest) => Promise; + postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise; + postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise; + putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise; + postAccountRequestOTP: () => Promise; + postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise; + postConvertToKeyConnector: () => Promise; + + getFolder: (id: string) => Promise; + postFolder: (request: FolderRequest) => Promise; + putFolder: (id: string, request: FolderRequest) => Promise; + deleteFolder: (id: string) => Promise; + + getSend: (id: string) => Promise; + postSendAccess: ( + id: string, + request: SendAccessRequest, + apiUrl?: string + ) => Promise; + getSends: () => Promise>; + postSend: (request: SendRequest) => Promise; + postFileTypeSend: (request: SendRequest) => Promise; + postSendFile: (sendId: string, fileId: string, data: FormData) => Promise; + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postSendFileLegacy: (data: FormData) => Promise; + putSend: (id: string, request: SendRequest) => Promise; + putSendRemovePassword: (id: string) => Promise; + deleteSend: (id: string) => Promise; + getSendFileDownloadData: ( + send: SendAccessView, + request: SendAccessRequest, + apiUrl?: string + ) => Promise; + renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise; + + getCipher: (id: string) => Promise; + getCipherAdmin: (id: string) => Promise; + getAttachmentData: ( + cipherId: string, + attachmentId: string, + emergencyAccessId?: string + ) => Promise; + getCiphersOrganization: (organizationId: string) => Promise>; + postCipher: (request: CipherRequest) => Promise; + postCipherCreate: (request: CipherCreateRequest) => Promise; + postCipherAdmin: (request: CipherCreateRequest) => Promise; + putCipher: (id: string, request: CipherRequest) => Promise; + putCipherAdmin: (id: string, request: CipherRequest) => Promise; + deleteCipher: (id: string) => Promise; + deleteCipherAdmin: (id: string) => Promise; + deleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise; + deleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise; + putMoveCiphers: (request: CipherBulkMoveRequest) => Promise; + putShareCipher: (id: string, request: CipherShareRequest) => Promise; + putShareCiphers: (request: CipherBulkShareRequest) => Promise; + putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise; + putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise; + postPurgeCiphers: (request: SecretVerificationRequest, organizationId?: string) => Promise; + postImportCiphers: (request: ImportCiphersRequest) => Promise; + postImportOrganizationCiphers: ( + organizationId: string, + request: ImportOrganizationCiphersRequest + ) => Promise; + putDeleteCipher: (id: string) => Promise; + putDeleteCipherAdmin: (id: string) => Promise; + putDeleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise; + putDeleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise; + putRestoreCipher: (id: string) => Promise; + putRestoreCipherAdmin: (id: string) => Promise; + putRestoreManyCiphers: ( + request: CipherBulkRestoreRequest + ) => Promise>; + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postCipherAttachmentLegacy: (id: string, data: FormData) => Promise; + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + postCipherAttachmentAdminLegacy: (id: string, data: FormData) => Promise; + postCipherAttachment: ( + id: string, + request: AttachmentRequest + ) => Promise; + deleteCipherAttachment: (id: string, attachmentId: string) => Promise; + deleteCipherAttachmentAdmin: (id: string, attachmentId: string) => Promise; + postShareCipherAttachment: ( + id: string, + attachmentId: string, + data: FormData, + organizationId: string + ) => Promise; + renewAttachmentUploadUrl: ( + id: string, + attachmentId: string + ) => Promise; + postAttachmentFile: (id: string, attachmentId: string, data: FormData) => Promise; + + getCollectionDetails: ( + organizationId: string, + id: string + ) => Promise; + getUserCollections: () => Promise>; + getCollections: (organizationId: string) => Promise>; + getCollectionUsers: (organizationId: string, id: string) => Promise; + postCollection: ( + organizationId: string, + request: CollectionRequest + ) => Promise; + putCollectionUsers: ( + organizationId: string, + id: string, + request: SelectionReadOnlyRequest[] + ) => Promise; + putCollection: ( + organizationId: string, + id: string, + request: CollectionRequest + ) => Promise; + deleteCollection: (organizationId: string, id: string) => Promise; + deleteCollectionUser: ( + organizationId: string, + id: string, + organizationUserId: string + ) => Promise; + + getGroupDetails: (organizationId: string, id: string) => Promise; + getGroups: (organizationId: string) => Promise>; + getGroupUsers: (organizationId: string, id: string) => Promise; + postGroup: (organizationId: string, request: GroupRequest) => Promise; + putGroup: (organizationId: string, id: string, request: GroupRequest) => Promise; + putGroupUsers: (organizationId: string, id: string, request: string[]) => Promise; + deleteGroup: (organizationId: string, id: string) => Promise; + deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise; + + getPolicy: (organizationId: string, type: PolicyType) => Promise; + getPolicies: (organizationId: string) => Promise>; + getPoliciesByToken: ( + organizationId: string, + token: string, + email: string, + organizationUserId: string + ) => Promise>; + getPoliciesByInvitedUser: ( + organizationId: string, + userId: string + ) => Promise>; + putPolicy: ( + organizationId: string, + type: PolicyType, + request: PolicyRequest + ) => Promise; + + getOrganizationUser: ( + organizationId: string, + id: string + ) => Promise; + getOrganizationUserGroups: (organizationId: string, id: string) => Promise; + getOrganizationUsers: ( + organizationId: string + ) => Promise>; + getOrganizationUserResetPasswordDetails: ( + organizationId: string, + id: string + ) => Promise; + postOrganizationUserInvite: ( + organizationId: string, + request: OrganizationUserInviteRequest + ) => Promise; + postOrganizationUserReinvite: (organizationId: string, id: string) => Promise; + postManyOrganizationUserReinvite: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + postOrganizationUserAccept: ( + organizationId: string, + id: string, + request: OrganizationUserAcceptRequest + ) => Promise; + postOrganizationUserConfirm: ( + organizationId: string, + id: string, + request: OrganizationUserConfirmRequest + ) => Promise; + postOrganizationUsersPublicKey: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + postOrganizationUserBulkConfirm: ( + organizationId: string, + request: OrganizationUserBulkConfirmRequest + ) => Promise>; + + putOrganizationUser: ( + organizationId: string, + id: string, + request: OrganizationUserUpdateRequest + ) => Promise; + putOrganizationUserGroups: ( + organizationId: string, + id: string, + request: OrganizationUserUpdateGroupsRequest + ) => Promise; + putOrganizationUserResetPasswordEnrollment: ( + organizationId: string, + userId: string, + request: OrganizationUserResetPasswordEnrollmentRequest + ) => Promise; + putOrganizationUserResetPassword: ( + organizationId: string, + id: string, + request: OrganizationUserResetPasswordRequest + ) => Promise; + deleteOrganizationUser: (organizationId: string, id: string) => Promise; + deleteManyOrganizationUsers: ( + organizationId: string, + request: OrganizationUserBulkRequest + ) => Promise>; + + getSync: () => Promise; + postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise; + postPublicImportDirectory: (request: OrganizationImportRequest) => Promise; + + getSettingsDomains: () => Promise; + putSettingsDomains: (request: UpdateDomainsRequest) => Promise; + + getTwoFactorProviders: () => Promise>; + getTwoFactorOrganizationProviders: ( + organizationId: string + ) => Promise>; + getTwoFactorAuthenticator: ( + request: SecretVerificationRequest + ) => Promise; + getTwoFactorEmail: (request: SecretVerificationRequest) => Promise; + getTwoFactorDuo: (request: SecretVerificationRequest) => Promise; + getTwoFactorOrganizationDuo: ( + organizationId: string, + request: SecretVerificationRequest + ) => Promise; + getTwoFactorYubiKey: (request: SecretVerificationRequest) => Promise; + getTwoFactorWebAuthn: (request: SecretVerificationRequest) => Promise; + getTwoFactorWebAuthnChallenge: (request: SecretVerificationRequest) => Promise; + getTwoFactorRecover: (request: SecretVerificationRequest) => Promise; + putTwoFactorAuthenticator: ( + request: UpdateTwoFactorAuthenticatorRequest + ) => Promise; + putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise; + putTwoFactorDuo: (request: UpdateTwoFactorDuoRequest) => Promise; + putTwoFactorOrganizationDuo: ( + organizationId: string, + request: UpdateTwoFactorDuoRequest + ) => Promise; + putTwoFactorYubiKey: ( + request: UpdateTwoFactorYubioOtpRequest + ) => Promise; + putTwoFactorWebAuthn: ( + request: UpdateTwoFactorWebAuthnRequest + ) => Promise; + deleteTwoFactorWebAuthn: ( + request: UpdateTwoFactorWebAuthnDeleteRequest + ) => Promise; + putTwoFactorDisable: (request: TwoFactorProviderRequest) => Promise; + putTwoFactorOrganizationDisable: ( + organizationId: string, + request: TwoFactorProviderRequest + ) => Promise; + postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise; + postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise; + postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise; + + getEmergencyAccessTrusted: () => Promise>; + getEmergencyAccessGranted: () => Promise>; + getEmergencyAccess: (id: string) => Promise; + getEmergencyGrantorPolicies: (id: string) => Promise>; + putEmergencyAccess: (id: string, request: EmergencyAccessUpdateRequest) => Promise; + deleteEmergencyAccess: (id: string) => Promise; + postEmergencyAccessInvite: (request: EmergencyAccessInviteRequest) => Promise; + postEmergencyAccessReinvite: (id: string) => Promise; + postEmergencyAccessAccept: (id: string, request: EmergencyAccessAcceptRequest) => Promise; + postEmergencyAccessConfirm: (id: string, request: EmergencyAccessConfirmRequest) => Promise; + postEmergencyAccessInitiate: (id: string) => Promise; + postEmergencyAccessApprove: (id: string) => Promise; + postEmergencyAccessReject: (id: string) => Promise; + postEmergencyAccessTakeover: (id: string) => Promise; + postEmergencyAccessPassword: ( + id: string, + request: EmergencyAccessPasswordRequest + ) => Promise; + postEmergencyAccessView: (id: string) => Promise; + + getOrganization: (id: string) => Promise; + getOrganizationBilling: (id: string) => Promise; + getOrganizationSubscription: (id: string) => Promise; + getOrganizationLicense: (id: string, installationId: string) => Promise; + getOrganizationTaxInfo: (id: string) => Promise; + getOrganizationAutoEnrollStatus: ( + identifier: string + ) => Promise; + getOrganizationSso: (id: string) => Promise; + postOrganization: (request: OrganizationCreateRequest) => Promise; + putOrganization: ( + id: string, + request: OrganizationUpdateRequest + ) => Promise; + putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise; + postLeaveOrganization: (id: string) => Promise; + postOrganizationLicense: (data: FormData) => Promise; + postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise; + postOrganizationApiKey: ( + id: string, + request: SecretVerificationRequest + ) => Promise; + postOrganizationRotateApiKey: ( + id: string, + request: SecretVerificationRequest + ) => Promise; + postOrganizationSso: ( + id: string, + request: OrganizationSsoRequest + ) => Promise; + postOrganizationUpgrade: ( + id: string, + request: OrganizationUpgradeRequest + ) => Promise; + postOrganizationUpdateSubscription: ( + id: string, + request: OrganizationSubscriptionUpdateRequest + ) => Promise; + postOrganizationSeat: (id: string, request: SeatRequest) => Promise; + postOrganizationStorage: (id: string, request: StorageRequest) => Promise; + postOrganizationPayment: (id: string, request: PaymentRequest) => Promise; + postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise; + postOrganizationCancel: (id: string) => Promise; + postOrganizationReinstate: (id: string) => Promise; + deleteOrganization: (id: string, request: SecretVerificationRequest) => Promise; + getPlans: () => Promise>; + getTaxRates: () => Promise>; + getOrganizationKeys: (id: string) => Promise; + postOrganizationKeys: ( + id: string, + request: OrganizationKeysRequest + ) => Promise; + + postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise; + getProvider: (id: string) => Promise; + putProvider: (id: string, request: ProviderUpdateRequest) => Promise; + + getProviderUsers: (providerId: string) => Promise>; + getProviderUser: (providerId: string, id: string) => Promise; + postProviderUserInvite: (providerId: string, request: ProviderUserInviteRequest) => Promise; + postProviderUserReinvite: (providerId: string, id: string) => Promise; + postManyProviderUserReinvite: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + postProviderUserAccept: ( + providerId: string, + id: string, + request: ProviderUserAcceptRequest + ) => Promise; + postProviderUserConfirm: ( + providerId: string, + id: string, + request: ProviderUserConfirmRequest + ) => Promise; + postProviderUsersPublicKey: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + postProviderUserBulkConfirm: ( + providerId: string, + request: ProviderUserBulkConfirmRequest + ) => Promise>; + putProviderUser: ( + providerId: string, + id: string, + request: ProviderUserUpdateRequest + ) => Promise; + deleteProviderUser: (organizationId: string, id: string) => Promise; + deleteManyProviderUsers: ( + providerId: string, + request: ProviderUserBulkRequest + ) => Promise>; + getProviderClients: ( + providerId: string + ) => Promise>; + postProviderAddOrganization: ( + providerId: string, + request: ProviderAddOrganizationRequest + ) => Promise; + postProviderCreateOrganization: ( + providerId: string, + request: ProviderOrganizationCreateRequest + ) => Promise; + deleteProviderOrganization: (providerId: string, organizationId: string) => Promise; + + getEvents: (start: string, end: string, token: string) => Promise>; + getEventsCipher: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsOrganization: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsOrganizationUser: ( + organizationId: string, + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsProvider: ( + id: string, + start: string, + end: string, + token: string + ) => Promise>; + getEventsProviderUser: ( + providerId: string, + id: string, + start: string, + end: string, + token: string + ) => Promise>; + postEventsCollect: (request: EventRequest[]) => Promise; + + deleteSsoUser: (organizationId: string) => Promise; + getSsoUserIdentifier: () => Promise; + + getUserPublicKey: (id: string) => Promise; + + getHibpBreach: (username: string) => Promise; + + postBitPayInvoice: (request: BitPayInvoiceRequest) => Promise; + postSetupPayment: () => Promise; + + getActiveBearerToken: () => Promise; + fetch: (request: Request) => Promise; + nativeFetch: (request: Request) => Promise; + + preValidateSso: (identifier: string) => Promise; + + postCreateSponsorship: ( + sponsorshipOrgId: string, + request: OrganizationSponsorshipCreateRequest + ) => Promise; + deleteRevokeSponsorship: (sponsoringOrganizationId: string) => Promise; + deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise; + postPreValidateSponsorshipToken: (sponsorshipToken: string) => Promise; + postRedeemSponsorship: ( + sponsorshipToken: string, + request: OrganizationSponsorshipRedeemRequest + ) => Promise; + postResendSponsorshipOffer: (sponsoringOrgId: string) => Promise; + + getUserKeyFromKeyConnector: (keyConnectorUrl: string) => Promise; + postUserKeyToKeyConnector: ( + keyConnectorUrl: string, + request: KeyConnectorUserKeyRequest + ) => Promise; + getKeyConnectorAlive: (keyConnectorUrl: string) => Promise; +} diff --git a/jslib/common/src/abstractions/appId.service.ts b/jslib/common/src/abstractions/appId.service.ts new file mode 100644 index 00000000..99bc6c9e --- /dev/null +++ b/jslib/common/src/abstractions/appId.service.ts @@ -0,0 +1,4 @@ +export abstract class AppIdService { + getAppId: () => Promise; + getAnonymousAppId: () => Promise; +} diff --git a/jslib/common/src/abstractions/audit.service.ts b/jslib/common/src/abstractions/audit.service.ts new file mode 100644 index 00000000..6b6b81f6 --- /dev/null +++ b/jslib/common/src/abstractions/audit.service.ts @@ -0,0 +1,6 @@ +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; + +export abstract class AuditService { + passwordLeaked: (password: string) => Promise; + breachedAccounts: (username: string) => Promise; +} diff --git a/jslib/common/src/abstractions/auth.service.ts b/jslib/common/src/abstractions/auth.service.ts new file mode 100644 index 00000000..bebac730 --- /dev/null +++ b/jslib/common/src/abstractions/auth.service.ts @@ -0,0 +1,25 @@ +import { AuthResult } from "../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; + +export abstract class AuthService { + masterPasswordHash: string; + email: string; + logIn: ( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ) => Promise; + logInTwoFactor: ( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ) => Promise; + logOut: (callback: () => void) => void; + makePreloginKey: (masterPassword: string, email: string) => Promise; + authingWithApiKey: () => boolean; + authingWithSso: () => boolean; + authingWithPassword: () => boolean; +} diff --git a/jslib/common/src/abstractions/broadcaster.service.ts b/jslib/common/src/abstractions/broadcaster.service.ts new file mode 100644 index 00000000..1b9d0899 --- /dev/null +++ b/jslib/common/src/abstractions/broadcaster.service.ts @@ -0,0 +1,5 @@ +export abstract class BroadcasterService { + send: (message: any, id?: string) => void; + subscribe: (id: string, messageCallback: (message: any) => any) => void; + unsubscribe: (id: string) => void; +} diff --git a/jslib/common/src/abstractions/cipher.service.ts b/jslib/common/src/abstractions/cipher.service.ts new file mode 100644 index 00000000..a87a6f0a --- /dev/null +++ b/jslib/common/src/abstractions/cipher.service.ts @@ -0,0 +1,79 @@ +import { CipherType } from "../enums/cipherType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherData } from "../models/data/cipherData"; +import { Cipher } from "../models/domain/cipher"; +import { Field } from "../models/domain/field"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; + +export abstract class CipherService { + clearCache: (userId?: string) => Promise; + encrypt: ( + model: CipherView, + key?: SymmetricCryptoKey, + originalCipher?: Cipher + ) => Promise; + encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise; + encryptField: (fieldModel: FieldView, key: SymmetricCryptoKey) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllDecryptedForGrouping: (groupingId: string, folder?: boolean) => Promise; + getAllDecryptedForUrl: ( + url: string, + includeOtherTypes?: CipherType[], + defaultMatch?: UriMatchType + ) => Promise; + getAllFromApiForOrganization: (organizationId: string) => Promise; + getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; + getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise; + getNextCipherForUrl: (url: string) => Promise; + updateLastUsedIndexForUrl: (url: string) => void; + updateLastUsedDate: (id: string) => Promise; + updateLastLaunchedDate: (id: string) => Promise; + saveNeverDomain: (domain: string) => Promise; + saveWithServer: (cipher: Cipher) => Promise; + shareWithServer: ( + cipher: CipherView, + organizationId: string, + collectionIds: string[] + ) => Promise; + shareManyWithServer: ( + ciphers: CipherView[], + organizationId: string, + collectionIds: string[] + ) => Promise; + saveAttachmentWithServer: ( + cipher: Cipher, + unencryptedFile: any, + admin?: boolean + ) => Promise; + saveAttachmentRawWithServer: ( + cipher: Cipher, + filename: string, + data: ArrayBuffer, + admin?: boolean + ) => Promise; + saveCollectionsWithServer: (cipher: Cipher) => Promise; + upsert: (cipher: CipherData | CipherData[]) => Promise; + replace: (ciphers: { [id: string]: CipherData }) => Promise; + clear: (userId: string) => Promise; + moveManyWithServer: (ids: string[], folderId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; + deleteManyWithServer: (ids: string[]) => Promise; + deleteAttachment: (id: string, attachmentId: string) => Promise; + deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise; + sortCiphersByLastUsed: (a: any, b: any) => number; + sortCiphersByLastUsedThenName: (a: any, b: any) => number; + getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number; + softDelete: (id: string | string[]) => Promise; + softDeleteWithServer: (id: string) => Promise; + softDeleteManyWithServer: (ids: string[]) => Promise; + restore: ( + cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[] + ) => Promise; + restoreWithServer: (id: string) => Promise; + restoreManyWithServer: (ids: string[]) => Promise; +} diff --git a/jslib/common/src/abstractions/collection.service.ts b/jslib/common/src/abstractions/collection.service.ts new file mode 100644 index 00000000..0673e906 --- /dev/null +++ b/jslib/common/src/abstractions/collection.service.ts @@ -0,0 +1,19 @@ +import { CollectionData } from "../models/data/collectionData"; +import { Collection } from "../models/domain/collection"; +import { TreeNode } from "../models/domain/treeNode"; +import { CollectionView } from "../models/view/collectionView"; + +export abstract class CollectionService { + clearCache: (userId?: string) => Promise; + encrypt: (model: CollectionView) => Promise; + decryptMany: (collections: Collection[]) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllNested: (collections?: CollectionView[]) => Promise[]>; + getNested: (id: string) => Promise>; + upsert: (collection: CollectionData | CollectionData[]) => Promise; + replace: (collections: { [id: string]: CollectionData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; +} diff --git a/jslib/common/src/abstractions/crypto.service.ts b/jslib/common/src/abstractions/crypto.service.ts new file mode 100644 index 00000000..bc61ba7e --- /dev/null +++ b/jslib/common/src/abstractions/crypto.service.ts @@ -0,0 +1,86 @@ +import { HashPurpose } from "../enums/hashPurpose"; +import { KdfType } from "../enums/kdfType"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "../models/response/profileProviderResponse"; + +export abstract class CryptoService { + setKey: (key: SymmetricCryptoKey) => Promise; + setKeyHash: (keyHash: string) => Promise; + setEncKey: (encKey: string) => Promise; + setEncPrivateKey: (encPrivateKey: string) => Promise; + setOrgKeys: ( + orgs: ProfileOrganizationResponse[], + providerOrgs: ProfileProviderOrganizationResponse[] + ) => Promise; + setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise; + getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; + getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise; + getKeyHash: () => Promise; + compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise; + getEncKey: (key?: SymmetricCryptoKey) => Promise; + getPublicKey: () => Promise; + getPrivateKey: () => Promise; + getFingerprint: (userId: string, publicKey?: ArrayBuffer) => Promise; + getOrgKeys: () => Promise>; + getOrgKey: (orgId: string) => Promise; + getProviderKey: (providerId: string) => Promise; + hasKey: () => Promise; + hasKeyInMemory: (userId?: string) => Promise; + hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; + hasEncKey: () => Promise; + clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise; + clearKeyHash: () => Promise; + clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise; + clearKeyPair: (memoryOnly?: boolean, userId?: string) => Promise; + clearOrgKeys: (memoryOnly?: boolean, userId?: string) => Promise; + clearProviderKeys: (memoryOnly?: boolean) => Promise; + clearPinProtectedKey: () => Promise; + clearKeys: (userId?: string) => Promise; + toggleKey: () => Promise; + makeKey: ( + password: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ) => Promise; + makeKeyFromPin: ( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number, + protectedKeyCs?: EncString + ) => Promise; + makeShareKey: () => Promise<[EncString, SymmetricCryptoKey]>; + makeKeyPair: (key?: SymmetricCryptoKey) => Promise<[string, EncString]>; + makePinKey: ( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ) => Promise; + makeSendKey: (keyMaterial: ArrayBuffer) => Promise; + hashPassword: ( + password: string, + key: SymmetricCryptoKey, + hashPurpose?: HashPurpose + ) => Promise; + makeEncKey: (key: SymmetricCryptoKey) => Promise<[SymmetricCryptoKey, EncString]>; + remakeEncKey: ( + key: SymmetricCryptoKey, + encKey?: SymmetricCryptoKey + ) => Promise<[SymmetricCryptoKey, EncString]>; + encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise; + encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; + rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise; + rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; + decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise; + decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise; + decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise; + randomNumber: (min: number, max: number) => Promise; + validateKey: (key: SymmetricCryptoKey) => Promise; +} diff --git a/jslib/common/src/abstractions/cryptoFunction.service.ts b/jslib/common/src/abstractions/cryptoFunction.service.ts new file mode 100644 index 00000000..21ed33cb --- /dev/null +++ b/jslib/common/src/abstractions/cryptoFunction.service.ts @@ -0,0 +1,62 @@ +import { DecryptParameters } from "../models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +export abstract class CryptoFunctionService { + pbkdf2: ( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ) => Promise; + hkdf: ( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ) => Promise; + hkdfExpand: ( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ) => Promise; + hash: ( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ) => Promise; + hmac: ( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ) => Promise; + compare: (a: ArrayBuffer, b: ArrayBuffer) => Promise; + hmacFast: ( + value: ArrayBuffer | string, + key: ArrayBuffer | string, + algorithm: "sha1" | "sha256" | "sha512" + ) => Promise; + compareFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise; + aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + aesDecryptFastParameters: ( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ) => DecryptParameters; + aesDecryptFast: (parameters: DecryptParameters) => Promise; + aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + rsaEncrypt: ( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ) => Promise; + rsaDecrypt: ( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ) => Promise; + rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise; + rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>; + randomBytes: (length: number) => Promise; +} diff --git a/jslib/common/src/abstractions/environment.service.ts b/jslib/common/src/abstractions/environment.service.ts new file mode 100644 index 00000000..8398b4c6 --- /dev/null +++ b/jslib/common/src/abstractions/environment.service.ts @@ -0,0 +1,34 @@ +import { Observable } from "rxjs"; + +export type Urls = { + base?: string; + webVault?: string; + api?: string; + identity?: string; + icons?: string; + notifications?: string; + events?: string; + keyConnector?: string; +}; + +export type PayPalConfig = { + businessId?: string; + buttonAction?: string; +}; + +export abstract class EnvironmentService { + urls: Observable; + + hasBaseUrl: () => boolean; + getNotificationsUrl: () => string; + getWebVaultUrl: () => string; + getSendUrl: () => string; + getIconsUrl: () => string; + getApiUrl: () => string; + getIdentityUrl: () => string; + getEventsUrl: () => string; + getKeyConnectorUrl: () => string; + setUrlsFromStorage: () => Promise; + setUrls: (urls: Urls) => Promise; + getUrls: () => Urls; +} diff --git a/jslib/common/src/abstractions/event.service.ts b/jslib/common/src/abstractions/event.service.ts new file mode 100644 index 00000000..2f7660fb --- /dev/null +++ b/jslib/common/src/abstractions/event.service.ts @@ -0,0 +1,7 @@ +import { EventType } from "../enums/eventType"; + +export abstract class EventService { + collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise; + uploadEvents: (userId?: string) => Promise; + clearEvents: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/export.service.ts b/jslib/common/src/abstractions/export.service.ts new file mode 100644 index 00000000..b0266530 --- /dev/null +++ b/jslib/common/src/abstractions/export.service.ts @@ -0,0 +1,11 @@ +import { EventView } from "../models/view/eventView"; + +export type ExportFormat = "csv" | "json" | "encrypted_json"; + +export abstract class ExportService { + getExport: (format?: ExportFormat, organizationId?: string) => Promise; + getPasswordProtectedExport: (password: string, organizationId?: string) => Promise; + getOrganizationExport: (organizationId: string, format?: ExportFormat) => Promise; + getEventExport: (events: EventView[]) => Promise; + getFileName: (prefix?: string, extension?: string) => string; +} diff --git a/jslib/common/src/abstractions/fileUpload.service.ts b/jslib/common/src/abstractions/fileUpload.service.ts new file mode 100644 index 00000000..fcd3c22c --- /dev/null +++ b/jslib/common/src/abstractions/fileUpload.service.ts @@ -0,0 +1,18 @@ +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; + +export abstract class FileUploadService { + uploadSendFile: ( + uploadData: SendFileUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) => Promise; + uploadCipherAttachment: ( + admin: boolean, + uploadData: AttachmentUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) => Promise; +} diff --git a/jslib/common/src/abstractions/folder.service.ts b/jslib/common/src/abstractions/folder.service.ts new file mode 100644 index 00000000..574cebfc --- /dev/null +++ b/jslib/common/src/abstractions/folder.service.ts @@ -0,0 +1,21 @@ +import { FolderData } from "../models/data/folderData"; +import { Folder } from "../models/domain/folder"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TreeNode } from "../models/domain/treeNode"; +import { FolderView } from "../models/view/folderView"; + +export abstract class FolderService { + clearCache: (userId?: string) => Promise; + encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + getAllNested: () => Promise[]>; + getNested: (id: string) => Promise>; + saveWithServer: (folder: Folder) => Promise; + upsert: (folder: FolderData | FolderData[]) => Promise; + replace: (folders: { [id: string]: FolderData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; +} diff --git a/jslib/common/src/abstractions/i18n.service.ts b/jslib/common/src/abstractions/i18n.service.ts new file mode 100644 index 00000000..4706e0d1 --- /dev/null +++ b/jslib/common/src/abstractions/i18n.service.ts @@ -0,0 +1,9 @@ +export abstract class I18nService { + locale: string; + supportedTranslationLocales: string[]; + translationLocale: string; + collator: Intl.Collator; + localeNames: Map; + t: (id: string, p1?: string, p2?: string, p3?: string) => string; + translate: (id: string, p1?: string, p2?: string, p3?: string) => string; +} diff --git a/jslib/common/src/abstractions/import.service.ts b/jslib/common/src/abstractions/import.service.ts new file mode 100644 index 00000000..01f099b3 --- /dev/null +++ b/jslib/common/src/abstractions/import.service.ts @@ -0,0 +1,19 @@ +import { ImportOption, ImportType } from "../enums/importOptions"; +import { ImportError } from "../importers/importError"; +import { Importer } from "../importers/importer"; + +export abstract class ImportService { + featuredImportOptions: readonly ImportOption[]; + regularImportOptions: readonly ImportOption[]; + getImportOptions: () => ImportOption[]; + import: ( + importer: Importer, + fileContents: string, + organizationId?: string + ) => Promise; + getImporter: ( + format: ImportType | "bitwardenpasswordprotected", + organizationId: string, + password?: string + ) => Importer; +} diff --git a/jslib/common/src/abstractions/keyConnector.service.ts b/jslib/common/src/abstractions/keyConnector.service.ts new file mode 100644 index 00000000..b8f3bf7c --- /dev/null +++ b/jslib/common/src/abstractions/keyConnector.service.ts @@ -0,0 +1,19 @@ +import { Organization } from "../models/domain/organization"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export abstract class KeyConnectorService { + getAndSetKey: (url?: string) => Promise; + getManagingOrganization: () => Promise; + getUsesKeyConnector: () => Promise; + migrateUser: () => Promise; + userNeedsMigration: () => Promise; + convertNewSsoUserToKeyConnector: ( + tokenResponse: IdentityTokenResponse, + orgId: string + ) => Promise; + setUsesKeyConnector: (enabled: boolean) => Promise; + setConvertAccountRequired: (status: boolean) => Promise; + getConvertAccountRequired: () => Promise; + removeConvertAccountRequired: () => Promise; + clear: () => Promise; +} diff --git a/jslib/common/src/abstractions/log.service.ts b/jslib/common/src/abstractions/log.service.ts new file mode 100644 index 00000000..9997e480 --- /dev/null +++ b/jslib/common/src/abstractions/log.service.ts @@ -0,0 +1,11 @@ +import { LogLevelType } from "../enums/logLevelType"; + +export abstract class LogService { + debug: (message: string) => void; + info: (message: string) => void; + warning: (message: string) => void; + error: (message: string) => void; + write: (level: LogLevelType, message: string) => void; + time: (label: string) => void; + timeEnd: (label: string) => [number, number]; +} diff --git a/jslib/common/src/abstractions/messaging.service.ts b/jslib/common/src/abstractions/messaging.service.ts new file mode 100644 index 00000000..7c5f05f9 --- /dev/null +++ b/jslib/common/src/abstractions/messaging.service.ts @@ -0,0 +1,3 @@ +export abstract class MessagingService { + send: (subscriber: string, arg?: any) => void; +} diff --git a/jslib/common/src/abstractions/notifications.service.ts b/jslib/common/src/abstractions/notifications.service.ts new file mode 100644 index 00000000..921e8e62 --- /dev/null +++ b/jslib/common/src/abstractions/notifications.service.ts @@ -0,0 +1,6 @@ +export abstract class NotificationsService { + init: () => Promise; + updateConnection: (sync?: boolean) => Promise; + reconnectFromActivity: () => Promise; + disconnectFromInactivity: () => Promise; +} diff --git a/jslib/common/src/abstractions/organization.service.ts b/jslib/common/src/abstractions/organization.service.ts new file mode 100644 index 00000000..e0b5fde4 --- /dev/null +++ b/jslib/common/src/abstractions/organization.service.ts @@ -0,0 +1,11 @@ +import { OrganizationData } from "../models/data/organizationData"; +import { Organization } from "../models/domain/organization"; + +export abstract class OrganizationService { + get: (id: string) => Promise; + getByIdentifier: (identifier: string) => Promise; + getAll: (userId?: string) => Promise; + save: (orgs: { [id: string]: OrganizationData }) => Promise; + canManageSponsorships: () => Promise; + hasOrganizations: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/passwordGeneration.service.ts b/jslib/common/src/abstractions/passwordGeneration.service.ts new file mode 100644 index 00000000..82bc021f --- /dev/null +++ b/jslib/common/src/abstractions/passwordGeneration.service.ts @@ -0,0 +1,20 @@ +import * as zxcvbn from "zxcvbn"; + +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { PasswordGeneratorPolicyOptions } from "../models/domain/passwordGeneratorPolicyOptions"; + +export abstract class PasswordGenerationService { + generatePassword: (options: any) => Promise; + generatePassphrase: (options: any) => Promise; + getOptions: () => Promise<[any, PasswordGeneratorPolicyOptions]>; + enforcePasswordGeneratorPoliciesOnOptions: ( + options: any + ) => Promise<[any, PasswordGeneratorPolicyOptions]>; + getPasswordGeneratorPolicyOptions: () => Promise; + saveOptions: (options: any) => Promise; + getHistory: () => Promise; + addHistory: (password: string) => Promise; + clear: (userId?: string) => Promise; + passwordStrength: (password: string, userInputs?: string[]) => zxcvbn.ZXCVBNResult; + normalizeOptions: (options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) => void; +} diff --git a/jslib/common/src/abstractions/passwordReprompt.service.ts b/jslib/common/src/abstractions/passwordReprompt.service.ts new file mode 100644 index 00000000..6253425b --- /dev/null +++ b/jslib/common/src/abstractions/passwordReprompt.service.ts @@ -0,0 +1,5 @@ +export abstract class PasswordRepromptService { + protectedFields: () => string[]; + showPasswordPrompt: () => Promise; + enabled: () => Promise; +} diff --git a/jslib/common/src/abstractions/platformUtils.service.ts b/jslib/common/src/abstractions/platformUtils.service.ts new file mode 100644 index 00000000..4a014868 --- /dev/null +++ b/jslib/common/src/abstractions/platformUtils.service.ts @@ -0,0 +1,52 @@ +import { ClientType } from "../enums/clientType"; +import { DeviceType } from "../enums/deviceType"; +import { ThemeType } from "../enums/themeType"; + +interface ToastOptions { + timeout?: number; +} + +export abstract class PlatformUtilsService { + getDevice: () => DeviceType; + getDeviceString: () => string; + getClientType: () => ClientType; + isFirefox: () => boolean; + isChrome: () => boolean; + isEdge: () => boolean; + isOpera: () => boolean; + isVivaldi: () => boolean; + isSafari: () => boolean; + isMacAppStore: () => boolean; + isViewOpen: () => Promise; + launchUri: (uri: string, options?: any) => void; + saveFile: (win: Window, blobData: any, blobOptions: any, fileName: string) => void; + getApplicationVersion: () => Promise; + supportsWebAuthn: (win: Window) => boolean; + supportsDuo: () => boolean; + showToast: ( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: ToastOptions + ) => void; + showDialog: ( + body: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string, + bodyIsHtml?: boolean + ) => Promise; + isDev: () => boolean; + isSelfHost: () => boolean; + copyToClipboard: (text: string, options?: any) => void | boolean; + readFromClipboard: (options?: any) => Promise; + supportsBiometric: () => Promise; + authenticateBiometric: () => Promise; + getDefaultSystemTheme: () => Promise; + onDefaultSystemThemeChange: ( + callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown + ) => unknown; + getEffectiveTheme: () => Promise; + supportsSecureStorage: () => boolean; +} diff --git a/jslib/common/src/abstractions/policy.service.ts b/jslib/common/src/abstractions/policy.service.ts new file mode 100644 index 00000000..24dcc996 --- /dev/null +++ b/jslib/common/src/abstractions/policy.service.ts @@ -0,0 +1,32 @@ +import { PolicyType } from "../enums/policyType"; +import { PolicyData } from "../models/data/policyData"; +import { MasterPasswordPolicyOptions } from "../models/domain/masterPasswordPolicyOptions"; +import { Policy } from "../models/domain/policy"; +import { ResetPasswordPolicyOptions } from "../models/domain/resetPasswordPolicyOptions"; +import { ListResponse } from "../models/response/listResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; + +export abstract class PolicyService { + clearCache: () => void; + getAll: (type?: PolicyType, userId?: string) => Promise; + getPolicyForOrganization: (policyType: PolicyType, organizationId: string) => Promise; + replace: (policies: { [id: string]: PolicyData }) => Promise; + clear: (userId?: string) => Promise; + getMasterPasswordPoliciesForInvitedUsers: (orgId: string) => Promise; + getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise; + evaluateMasterPassword: ( + passwordStrength: number, + newPassword: string, + enforcedPolicyOptions?: MasterPasswordPolicyOptions + ) => boolean; + getResetPasswordPolicyOptions: ( + policies: Policy[], + orgId: string + ) => [ResetPasswordPolicyOptions, boolean]; + mapPoliciesFromToken: (policiesResponse: ListResponse) => Policy[]; + policyAppliesToUser: ( + policyType: PolicyType, + policyFilter?: (policy: Policy) => boolean, + userId?: string + ) => Promise; +} diff --git a/jslib/common/src/abstractions/provider.service.ts b/jslib/common/src/abstractions/provider.service.ts new file mode 100644 index 00000000..e4746369 --- /dev/null +++ b/jslib/common/src/abstractions/provider.service.ts @@ -0,0 +1,8 @@ +import { ProviderData } from "../models/data/providerData"; +import { Provider } from "../models/domain/provider"; + +export abstract class ProviderService { + get: (id: string) => Promise; + getAll: () => Promise; + save: (providers: { [id: string]: ProviderData }) => Promise; +} diff --git a/jslib/common/src/abstractions/search.service.ts b/jslib/common/src/abstractions/search.service.ts new file mode 100644 index 00000000..9c2b0eeb --- /dev/null +++ b/jslib/common/src/abstractions/search.service.ts @@ -0,0 +1,16 @@ +import { CipherView } from "../models/view/cipherView"; +import { SendView } from "../models/view/sendView"; + +export abstract class SearchService { + indexedEntityId?: string = null; + clearIndex: () => void; + isSearchable: (query: string) => boolean; + indexCiphers: (indexedEntityGuid?: string, ciphersToIndex?: CipherView[]) => Promise; + searchCiphers: ( + query: string, + filter?: ((cipher: CipherView) => boolean) | ((cipher: CipherView) => boolean)[], + ciphers?: CipherView[] + ) => Promise; + searchCiphersBasic: (ciphers: CipherView[], query: string, deleted?: boolean) => CipherView[]; + searchSends: (sends: SendView[], query: string) => SendView[]; +} diff --git a/jslib/common/src/abstractions/send.service.ts b/jslib/common/src/abstractions/send.service.ts new file mode 100644 index 00000000..3331e41e --- /dev/null +++ b/jslib/common/src/abstractions/send.service.ts @@ -0,0 +1,25 @@ +import { SendData } from "../models/data/sendData"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { Send } from "../models/domain/send"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SendView } from "../models/view/sendView"; + +export abstract class SendService { + clearCache: () => Promise; + encrypt: ( + model: SendView, + file: File | ArrayBuffer, + password: string, + key?: SymmetricCryptoKey + ) => Promise<[Send, EncArrayBuffer]>; + get: (id: string) => Promise; + getAll: () => Promise; + getAllDecrypted: () => Promise; + saveWithServer: (sendData: [Send, EncArrayBuffer]) => Promise; + upsert: (send: SendData | SendData[]) => Promise; + replace: (sends: { [id: string]: SendData }) => Promise; + clear: (userId: string) => Promise; + delete: (id: string | string[]) => Promise; + deleteWithServer: (id: string) => Promise; + removePasswordWithServer: (id: string) => Promise; +} diff --git a/jslib/common/src/abstractions/settings.service.ts b/jslib/common/src/abstractions/settings.service.ts new file mode 100644 index 00000000..e7886585 --- /dev/null +++ b/jslib/common/src/abstractions/settings.service.ts @@ -0,0 +1,6 @@ +export abstract class SettingsService { + clearCache: () => Promise; + getEquivalentDomains: () => Promise; + setEquivalentDomains: (equivalentDomains: string[][]) => Promise; + clear: (userId?: string) => Promise; +} diff --git a/jslib/common/src/abstractions/state.service.ts b/jslib/common/src/abstractions/state.service.ts new file mode 100644 index 00000000..ebf94df3 --- /dev/null +++ b/jslib/common/src/abstractions/state.service.ts @@ -0,0 +1,307 @@ +import { BehaviorSubject } from "rxjs"; + +import { KdfType } from "../enums/kdfType"; +import { ThemeType } from "../enums/themeType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account } from "../models/domain/account"; +import { EncString } from "../models/domain/encString"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { Policy } from "../models/domain/policy"; +import { StorageOptions } from "../models/domain/storageOptions"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { WindowState } from "../models/domain/windowState"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FolderView } from "../models/view/folderView"; +import { SendView } from "../models/view/sendView"; + +export abstract class StateService { + accounts: BehaviorSubject<{ [userId: string]: T }>; + activeAccount: BehaviorSubject; + + addAccount: (account: T) => Promise; + setActiveUser: (userId: string) => Promise; + clean: (options?: StorageOptions) => Promise; + init: () => Promise; + + getAccessToken: (options?: StorageOptions) => Promise; + setAccessToken: (value: string, options?: StorageOptions) => Promise; + getAddEditCipherInfo: (options?: StorageOptions) => Promise; + setAddEditCipherInfo: (value: any, options?: StorageOptions) => Promise; + getAlwaysShowDock: (options?: StorageOptions) => Promise; + setAlwaysShowDock: (value: boolean, options?: StorageOptions) => Promise; + getApiKeyClientId: (options?: StorageOptions) => Promise; + setApiKeyClientId: (value: string, options?: StorageOptions) => Promise; + getApiKeyClientSecret: (options?: StorageOptions) => Promise; + setApiKeyClientSecret: (value: string, options?: StorageOptions) => Promise; + getAutoConfirmFingerPrints: (options?: StorageOptions) => Promise; + setAutoConfirmFingerprints: (value: boolean, options?: StorageOptions) => Promise; + getAutoFillOnPageLoadDefault: (options?: StorageOptions) => Promise; + setAutoFillOnPageLoadDefault: (value: boolean, options?: StorageOptions) => Promise; + getBiometricAwaitingAcceptance: (options?: StorageOptions) => Promise; + setBiometricAwaitingAcceptance: (value: boolean, options?: StorageOptions) => Promise; + getBiometricFingerprintValidated: (options?: StorageOptions) => Promise; + setBiometricFingerprintValidated: (value: boolean, options?: StorageOptions) => Promise; + getBiometricLocked: (options?: StorageOptions) => Promise; + setBiometricLocked: (value: boolean, options?: StorageOptions) => Promise; + getBiometricText: (options?: StorageOptions) => Promise; + setBiometricText: (value: string, options?: StorageOptions) => Promise; + getBiometricUnlock: (options?: StorageOptions) => Promise; + setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise; + getCanAccessPremium: (options?: StorageOptions) => Promise; + getClearClipboard: (options?: StorageOptions) => Promise; + setClearClipboard: (value: number, options?: StorageOptions) => Promise; + getCollapsedGroupings: (options?: StorageOptions) => Promise; + setCollapsedGroupings: (value: string[], options?: StorageOptions) => Promise; + getConvertAccountToKeyConnector: (options?: StorageOptions) => Promise; + setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise; + getCryptoMasterKey: (options?: StorageOptions) => Promise; + setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise; + setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyB64: (options?: StorageOptions) => Promise; + setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise; + getDecodedToken: (options?: StorageOptions) => Promise; + setDecodedToken: (value: any, options?: StorageOptions) => Promise; + getDecryptedCiphers: (options?: StorageOptions) => Promise; + setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise; + getDecryptedCollections: (options?: StorageOptions) => Promise; + setDecryptedCollections: (value: CollectionView[], options?: StorageOptions) => Promise; + getDecryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise; + setDecryptedCryptoSymmetricKey: ( + value: SymmetricCryptoKey, + options?: StorageOptions + ) => Promise; + getDecryptedFolders: (options?: StorageOptions) => Promise; + setDecryptedFolders: (value: FolderView[], options?: StorageOptions) => Promise; + getDecryptedOrganizationKeys: ( + options?: StorageOptions + ) => Promise>; + setDecryptedOrganizationKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getDecryptedPasswordGenerationHistory: ( + options?: StorageOptions + ) => Promise; + setDecryptedPasswordGenerationHistory: ( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ) => Promise; + getDecryptedPinProtected: (options?: StorageOptions) => Promise; + setDecryptedPinProtected: (value: EncString, options?: StorageOptions) => Promise; + getDecryptedPolicies: (options?: StorageOptions) => Promise; + setDecryptedPolicies: (value: Policy[], options?: StorageOptions) => Promise; + getDecryptedPrivateKey: (options?: StorageOptions) => Promise; + setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getDecryptedProviderKeys: (options?: StorageOptions) => Promise>; + setDecryptedProviderKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getDecryptedSends: (options?: StorageOptions) => Promise; + setDecryptedSends: (value: SendView[], options?: StorageOptions) => Promise; + getDefaultUriMatch: (options?: StorageOptions) => Promise; + setDefaultUriMatch: (value: UriMatchType, options?: StorageOptions) => Promise; + getDisableAddLoginNotification: (options?: StorageOptions) => Promise; + setDisableAddLoginNotification: (value: boolean, options?: StorageOptions) => Promise; + getDisableAutoBiometricsPrompt: (options?: StorageOptions) => Promise; + setDisableAutoBiometricsPrompt: (value: boolean, options?: StorageOptions) => Promise; + getDisableAutoTotpCopy: (options?: StorageOptions) => Promise; + setDisableAutoTotpCopy: (value: boolean, options?: StorageOptions) => Promise; + getDisableBadgeCounter: (options?: StorageOptions) => Promise; + setDisableBadgeCounter: (value: boolean, options?: StorageOptions) => Promise; + getDisableChangedPasswordNotification: (options?: StorageOptions) => Promise; + setDisableChangedPasswordNotification: ( + value: boolean, + options?: StorageOptions + ) => Promise; + getDisableContextMenuItem: (options?: StorageOptions) => Promise; + setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise; + getDisableFavicon: (options?: StorageOptions) => Promise; + setDisableFavicon: (value: boolean, options?: StorageOptions) => Promise; + getDisableGa: (options?: StorageOptions) => Promise; + setDisableGa: (value: boolean, options?: StorageOptions) => Promise; + getDontShowCardsCurrentTab: (options?: StorageOptions) => Promise; + setDontShowCardsCurrentTab: (value: boolean, options?: StorageOptions) => Promise; + getDontShowIdentitiesCurrentTab: (options?: StorageOptions) => Promise; + setDontShowIdentitiesCurrentTab: (value: boolean, options?: StorageOptions) => Promise; + getEmail: (options?: StorageOptions) => Promise; + setEmail: (value: string, options?: StorageOptions) => Promise; + getEmailVerified: (options?: StorageOptions) => Promise; + setEmailVerified: (value: boolean, options?: StorageOptions) => Promise; + getEnableAlwaysOnTop: (options?: StorageOptions) => Promise; + setEnableAlwaysOnTop: (value: boolean, options?: StorageOptions) => Promise; + getEnableAutoFillOnPageLoad: (options?: StorageOptions) => Promise; + setEnableAutoFillOnPageLoad: (value: boolean, options?: StorageOptions) => Promise; + getEnableBiometric: (options?: StorageOptions) => Promise; + setEnableBiometric: (value: boolean, options?: StorageOptions) => Promise; + getEnableBrowserIntegration: (options?: StorageOptions) => Promise; + setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise; + getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise; + setEnableBrowserIntegrationFingerprint: ( + value: boolean, + options?: StorageOptions + ) => Promise; + getEnableCloseToTray: (options?: StorageOptions) => Promise; + setEnableCloseToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableFullWidth: (options?: StorageOptions) => Promise; + setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise; + getEnableGravitars: (options?: StorageOptions) => Promise; + setEnableGravitars: (value: boolean, options?: StorageOptions) => Promise; + getEnableMinimizeToTray: (options?: StorageOptions) => Promise; + setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableStartToTray: (options?: StorageOptions) => Promise; + setEnableStartToTray: (value: boolean, options?: StorageOptions) => Promise; + getEnableTray: (options?: StorageOptions) => Promise; + setEnableTray: (value: boolean, options?: StorageOptions) => Promise; + getEncryptedCiphers: (options?: StorageOptions) => Promise<{ [id: string]: CipherData }>; + setEncryptedCiphers: ( + value: { [id: string]: CipherData }, + options?: StorageOptions + ) => Promise; + getEncryptedCollections: (options?: StorageOptions) => Promise<{ [id: string]: CollectionData }>; + setEncryptedCollections: ( + value: { [id: string]: CollectionData }, + options?: StorageOptions + ) => Promise; + getEncryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise; + setEncryptedCryptoSymmetricKey: (value: string, options?: StorageOptions) => Promise; + getEncryptedFolders: (options?: StorageOptions) => Promise<{ [id: string]: FolderData }>; + setEncryptedFolders: ( + value: { [id: string]: FolderData }, + options?: StorageOptions + ) => Promise; + getEncryptedOrganizationKeys: (options?: StorageOptions) => Promise; + setEncryptedOrganizationKeys: ( + value: Map, + options?: StorageOptions + ) => Promise; + getEncryptedPasswordGenerationHistory: ( + options?: StorageOptions + ) => Promise; + setEncryptedPasswordGenerationHistory: ( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ) => Promise; + getEncryptedPinProtected: (options?: StorageOptions) => Promise; + setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise; + getEncryptedPolicies: (options?: StorageOptions) => Promise<{ [id: string]: PolicyData }>; + setEncryptedPolicies: ( + value: { [id: string]: PolicyData }, + options?: StorageOptions + ) => Promise; + getEncryptedPrivateKey: (options?: StorageOptions) => Promise; + setEncryptedPrivateKey: (value: string, options?: StorageOptions) => Promise; + getEncryptedProviderKeys: (options?: StorageOptions) => Promise; + setEncryptedProviderKeys: (value: any, options?: StorageOptions) => Promise; + getEncryptedSends: (options?: StorageOptions) => Promise<{ [id: string]: SendData }>; + setEncryptedSends: (value: { [id: string]: SendData }, options?: StorageOptions) => Promise; + getEntityId: (options?: StorageOptions) => Promise; + setEntityId: (value: string, options?: StorageOptions) => Promise; + getEntityType: (options?: StorageOptions) => Promise; + setEntityType: (value: string, options?: StorageOptions) => Promise; + getEnvironmentUrls: (options?: StorageOptions) => Promise; + setEnvironmentUrls: (value: EnvironmentUrls, options?: StorageOptions) => Promise; + getEquivalentDomains: (options?: StorageOptions) => Promise; + setEquivalentDomains: (value: string, options?: StorageOptions) => Promise; + getEventCollection: (options?: StorageOptions) => Promise; + setEventCollection: (value: EventData[], options?: StorageOptions) => Promise; + getEverBeenUnlocked: (options?: StorageOptions) => Promise; + setEverBeenUnlocked: (value: boolean, options?: StorageOptions) => Promise; + getForcePasswordReset: (options?: StorageOptions) => Promise; + setForcePasswordReset: (value: boolean, options?: StorageOptions) => Promise; + getInstalledVersion: (options?: StorageOptions) => Promise; + setInstalledVersion: (value: string, options?: StorageOptions) => Promise; + getIsAuthenticated: (options?: StorageOptions) => Promise; + getKdfIterations: (options?: StorageOptions) => Promise; + setKdfIterations: (value: number, options?: StorageOptions) => Promise; + getKdfType: (options?: StorageOptions) => Promise; + setKdfType: (value: KdfType, options?: StorageOptions) => Promise; + getKeyHash: (options?: StorageOptions) => Promise; + setKeyHash: (value: string, options?: StorageOptions) => Promise; + getLastActive: (options?: StorageOptions) => Promise; + setLastActive: (value: number, options?: StorageOptions) => Promise; + getLastSync: (options?: StorageOptions) => Promise; + setLastSync: (value: string, options?: StorageOptions) => Promise; + getLegacyEtmKey: (options?: StorageOptions) => Promise; + setLegacyEtmKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + getLocalData: (options?: StorageOptions) => Promise; + setLocalData: (value: string, options?: StorageOptions) => Promise; + getLocale: (options?: StorageOptions) => Promise; + setLocale: (value: string, options?: StorageOptions) => Promise; + getLoginRedirect: (options?: StorageOptions) => Promise; + setLoginRedirect: (value: any, options?: StorageOptions) => Promise; + getMainWindowSize: (options?: StorageOptions) => Promise; + setMainWindowSize: (value: number, options?: StorageOptions) => Promise; + getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise; + setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise; + getNeverDomains: (options?: StorageOptions) => Promise<{ [id: string]: any }>; + setNeverDomains: (value: { [id: string]: any }, options?: StorageOptions) => Promise; + getNoAutoPromptBiometrics: (options?: StorageOptions) => Promise; + setNoAutoPromptBiometrics: (value: boolean, options?: StorageOptions) => Promise; + getNoAutoPromptBiometricsText: (options?: StorageOptions) => Promise; + setNoAutoPromptBiometricsText: (value: string, options?: StorageOptions) => Promise; + getOpenAtLogin: (options?: StorageOptions) => Promise; + setOpenAtLogin: (value: boolean, options?: StorageOptions) => Promise; + getOrganizationInvitation: (options?: StorageOptions) => Promise; + setOrganizationInvitation: (value: any, options?: StorageOptions) => Promise; + getOrganizations: (options?: StorageOptions) => Promise<{ [id: string]: OrganizationData }>; + setOrganizations: ( + value: { [id: string]: OrganizationData }, + options?: StorageOptions + ) => Promise; + getPasswordGenerationOptions: (options?: StorageOptions) => Promise; + setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise; + getUsernameGenerationOptions: (options?: StorageOptions) => Promise; + setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise; + getGeneratorOptions: (options?: StorageOptions) => Promise; + setGeneratorOptions: (value: any, options?: StorageOptions) => Promise; + getProtectedPin: (options?: StorageOptions) => Promise; + setProtectedPin: (value: string, options?: StorageOptions) => Promise; + getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>; + setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise; + getPublicKey: (options?: StorageOptions) => Promise; + setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getRefreshToken: (options?: StorageOptions) => Promise; + setRefreshToken: (value: string, options?: StorageOptions) => Promise; + getRememberedEmail: (options?: StorageOptions) => Promise; + setRememberedEmail: (value: string, options?: StorageOptions) => Promise; + getSecurityStamp: (options?: StorageOptions) => Promise; + setSecurityStamp: (value: string, options?: StorageOptions) => Promise; + getSettings: (options?: StorageOptions) => Promise; + setSettings: (value: string, options?: StorageOptions) => Promise; + getSsoCodeVerifier: (options?: StorageOptions) => Promise; + setSsoCodeVerifier: (value: string, options?: StorageOptions) => Promise; + getSsoOrgIdentifier: (options?: StorageOptions) => Promise; + setSsoOrganizationIdentifier: (value: string, options?: StorageOptions) => Promise; + getSsoState: (options?: StorageOptions) => Promise; + setSsoState: (value: string, options?: StorageOptions) => Promise; + getTheme: (options?: StorageOptions) => Promise; + setTheme: (value: ThemeType, options?: StorageOptions) => Promise; + getTwoFactorToken: (options?: StorageOptions) => Promise; + setTwoFactorToken: (value: string, options?: StorageOptions) => Promise; + getUserId: (options?: StorageOptions) => Promise; + getUsesKeyConnector: (options?: StorageOptions) => Promise; + setUsesKeyConnector: (vaule: boolean, options?: StorageOptions) => Promise; + getVaultTimeout: (options?: StorageOptions) => Promise; + setVaultTimeout: (value: number, options?: StorageOptions) => Promise; + getVaultTimeoutAction: (options?: StorageOptions) => Promise; + setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise; + getStateVersion: () => Promise; + setStateVersion: (value: number) => Promise; + getWindow: () => Promise; + setWindow: (value: WindowState) => Promise; +} diff --git a/jslib/common/src/abstractions/stateMigration.service.ts b/jslib/common/src/abstractions/stateMigration.service.ts new file mode 100644 index 00000000..f16777a1 --- /dev/null +++ b/jslib/common/src/abstractions/stateMigration.service.ts @@ -0,0 +1,4 @@ +export abstract class StateMigrationService { + needsMigration: () => Promise; + migrate: () => Promise; +} diff --git a/jslib/common/src/abstractions/storage.service.ts b/jslib/common/src/abstractions/storage.service.ts new file mode 100644 index 00000000..f522d3cf --- /dev/null +++ b/jslib/common/src/abstractions/storage.service.ts @@ -0,0 +1,8 @@ +import { StorageOptions } from "../models/domain/storageOptions"; + +export abstract class StorageService { + get: (key: string, options?: StorageOptions) => Promise; + has: (key: string, options?: StorageOptions) => Promise; + save: (key: string, obj: any, options?: StorageOptions) => Promise; + remove: (key: string, options?: StorageOptions) => Promise; +} diff --git a/jslib/common/src/abstractions/sync.service.ts b/jslib/common/src/abstractions/sync.service.ts new file mode 100644 index 00000000..936d027f --- /dev/null +++ b/jslib/common/src/abstractions/sync.service.ts @@ -0,0 +1,19 @@ +import { + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; + +export abstract class SyncService { + syncInProgress: boolean; + + getLastSync: () => Promise; + setLastSync: (date: Date, userId?: string) => Promise; + fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise; + syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise; + syncDeleteFolder: (notification: SyncFolderNotification) => Promise; + syncUpsertCipher: (notification: SyncCipherNotification, isEdit: boolean) => Promise; + syncDeleteCipher: (notification: SyncFolderNotification) => Promise; + syncUpsertSend: (notification: SyncSendNotification, isEdit: boolean) => Promise; + syncDeleteSend: (notification: SyncSendNotification) => Promise; +} diff --git a/jslib/common/src/abstractions/system.service.ts b/jslib/common/src/abstractions/system.service.ts new file mode 100644 index 00000000..b5c1e595 --- /dev/null +++ b/jslib/common/src/abstractions/system.service.ts @@ -0,0 +1,6 @@ +export abstract class SystemService { + startProcessReload: () => Promise; + cancelProcessReload: () => void; + clearClipboard: (clipboardValue: string, timeoutMs?: number) => Promise; + clearPendingClipboard: () => Promise; +} diff --git a/jslib/common/src/abstractions/token.service.ts b/jslib/common/src/abstractions/token.service.ts new file mode 100644 index 00000000..f0470494 --- /dev/null +++ b/jslib/common/src/abstractions/token.service.ts @@ -0,0 +1,32 @@ +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export abstract class TokenService { + setTokens: ( + accessToken: string, + refreshToken: string, + clientIdClientSecret: [string, string] + ) => Promise; + setToken: (token: string) => Promise; + getToken: () => Promise; + setRefreshToken: (refreshToken: string) => Promise; + getRefreshToken: () => Promise; + setClientId: (clientId: string) => Promise; + getClientId: () => Promise; + setClientSecret: (clientSecret: string) => Promise; + getClientSecret: () => Promise; + setTwoFactorToken: (tokenResponse: IdentityTokenResponse) => Promise; + getTwoFactorToken: () => Promise; + clearTwoFactorToken: () => Promise; + clearToken: (userId?: string) => Promise; + decodeToken: (token?: string) => any; + getTokenExpirationDate: () => Promise; + tokenSecondsRemaining: (offsetSeconds?: number) => Promise; + tokenNeedsRefresh: (minutes?: number) => Promise; + getUserId: () => Promise; + getEmail: () => Promise; + getEmailVerified: () => Promise; + getName: () => Promise; + getPremium: () => Promise; + getIssuer: () => Promise; + getIsExternal: () => Promise; +} diff --git a/jslib/common/src/abstractions/totp.service.ts b/jslib/common/src/abstractions/totp.service.ts new file mode 100644 index 00000000..bf143e26 --- /dev/null +++ b/jslib/common/src/abstractions/totp.service.ts @@ -0,0 +1,5 @@ +export abstract class TotpService { + getCode: (key: string) => Promise; + getTimeInterval: (key: string) => number; + isAutoCopyEnabled: () => Promise; +} diff --git a/jslib/common/src/abstractions/twoFactor.service.ts b/jslib/common/src/abstractions/twoFactor.service.ts new file mode 100644 index 00000000..74f09167 --- /dev/null +++ b/jslib/common/src/abstractions/twoFactor.service.ts @@ -0,0 +1,23 @@ +import { TwoFactorProviderType } from "../enums/twoFactorProviderType"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; + +export interface TwoFactorProviderDetails { + type: TwoFactorProviderType; + name: string; + description: string; + priority: number; + sort: number; + premium: boolean; +} + +export abstract class TwoFactorService { + init: () => void; + getSupportedProviders: (win: Window) => TwoFactorProviderDetails[]; + getDefaultProvider: (webAuthnSupported: boolean) => TwoFactorProviderType; + setSelectedProvider: (type: TwoFactorProviderType) => void; + clearSelectedProvider: () => void; + + setProviders: (response: IdentityTwoFactorResponse) => void; + clearProviders: () => void; + getProviders: () => Map; +} diff --git a/jslib/common/src/abstractions/userVerification.service.ts b/jslib/common/src/abstractions/userVerification.service.ts new file mode 100644 index 00000000..ebd7d1f3 --- /dev/null +++ b/jslib/common/src/abstractions/userVerification.service.ts @@ -0,0 +1,12 @@ +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { Verification } from "../types/verification"; + +export abstract class UserVerificationService { + buildRequest: ( + verification: Verification, + requestClass?: new () => T, + alreadyHashed?: boolean + ) => Promise; + verifyUser: (verification: Verification) => Promise; + requestOTP: () => Promise; +} diff --git a/jslib/common/src/abstractions/usernameGeneration.service.ts b/jslib/common/src/abstractions/usernameGeneration.service.ts new file mode 100644 index 00000000..baba74d8 --- /dev/null +++ b/jslib/common/src/abstractions/usernameGeneration.service.ts @@ -0,0 +1,8 @@ +export abstract class UsernameGenerationService { + generateUsername: (options: any) => Promise; + generateWord: (options: any) => Promise; + generateSubaddress: (options: any) => Promise; + generateCatchall: (options: any) => Promise; + getOptions: () => Promise; + saveOptions: (options: any) => Promise; +} diff --git a/jslib/common/src/abstractions/vaultTimeout.service.ts b/jslib/common/src/abstractions/vaultTimeout.service.ts new file mode 100644 index 00000000..2c2b2521 --- /dev/null +++ b/jslib/common/src/abstractions/vaultTimeout.service.ts @@ -0,0 +1,11 @@ +export abstract class VaultTimeoutService { + isLocked: (userId?: string) => Promise; + checkVaultTimeout: () => Promise; + lock: (allowSoftLock?: boolean, userId?: string) => Promise; + logOut: (userId?: string) => Promise; + setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise; + getVaultTimeout: () => Promise; + isPinLockSet: () => Promise<[boolean, boolean]>; + isBiometricLockSet: () => Promise; + clear: (userId?: string) => Promise; +} diff --git a/jslib/common/src/enums/authenticationStatus.ts b/jslib/common/src/enums/authenticationStatus.ts new file mode 100644 index 00000000..8e1db548 --- /dev/null +++ b/jslib/common/src/enums/authenticationStatus.ts @@ -0,0 +1,6 @@ +export enum AuthenticationStatus { + Locked = "locked", + Unlocked = "unlocked", + LoggedOut = "loggedOut", + Active = "active", +} diff --git a/jslib/common/src/enums/authenticationType.ts b/jslib/common/src/enums/authenticationType.ts new file mode 100644 index 00000000..ed7375c8 --- /dev/null +++ b/jslib/common/src/enums/authenticationType.ts @@ -0,0 +1,5 @@ +export enum AuthenticationType { + Password = 0, + Sso = 1, + Api = 2, +} diff --git a/jslib/common/src/enums/cipherRepromptType.ts b/jslib/common/src/enums/cipherRepromptType.ts new file mode 100644 index 00000000..1d0a523c --- /dev/null +++ b/jslib/common/src/enums/cipherRepromptType.ts @@ -0,0 +1,4 @@ +export enum CipherRepromptType { + None = 0, + Password = 1, +} diff --git a/jslib/common/src/enums/cipherType.ts b/jslib/common/src/enums/cipherType.ts new file mode 100644 index 00000000..cce7874d --- /dev/null +++ b/jslib/common/src/enums/cipherType.ts @@ -0,0 +1,6 @@ +export enum CipherType { + Login = 1, + SecureNote = 2, + Card = 3, + Identity = 4, +} diff --git a/jslib/common/src/enums/clientType.ts b/jslib/common/src/enums/clientType.ts new file mode 100644 index 00000000..246769eb --- /dev/null +++ b/jslib/common/src/enums/clientType.ts @@ -0,0 +1,8 @@ +export enum ClientType { + Web = "web", + Browser = "browser", + Desktop = "desktop", + Mobile = "mobile", + Cli = "cli", + DirectoryConnector = "connector", +} diff --git a/jslib/common/src/enums/deviceType.ts b/jslib/common/src/enums/deviceType.ts new file mode 100644 index 00000000..707f83d8 --- /dev/null +++ b/jslib/common/src/enums/deviceType.ts @@ -0,0 +1,23 @@ +export enum DeviceType { + Android = 0, + iOS = 1, + ChromeExtension = 2, + FirefoxExtension = 3, + OperaExtension = 4, + EdgeExtension = 5, + WindowsDesktop = 6, + MacOsDesktop = 7, + LinuxDesktop = 8, + ChromeBrowser = 9, + FirefoxBrowser = 10, + OperaBrowser = 11, + EdgeBrowser = 12, + IEBrowser = 13, + UnknownBrowser = 14, + AndroidAmazon = 15, + UWP = 16, + SafariBrowser = 17, + VivaldiBrowser = 18, + VivaldiExtension = 19, + SafariExtension = 20, +} diff --git a/jslib/common/src/enums/emergencyAccessStatusType.ts b/jslib/common/src/enums/emergencyAccessStatusType.ts new file mode 100644 index 00000000..94400f34 --- /dev/null +++ b/jslib/common/src/enums/emergencyAccessStatusType.ts @@ -0,0 +1,7 @@ +export enum EmergencyAccessStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, + RecoveryInitiated = 3, + RecoveryApproved = 4, +} diff --git a/jslib/common/src/enums/emergencyAccessType.ts b/jslib/common/src/enums/emergencyAccessType.ts new file mode 100644 index 00000000..61a366c4 --- /dev/null +++ b/jslib/common/src/enums/emergencyAccessType.ts @@ -0,0 +1,4 @@ +export enum EmergencyAccessType { + View = 0, + Takeover = 1, +} diff --git a/jslib/common/src/enums/encryptionType.ts b/jslib/common/src/enums/encryptionType.ts new file mode 100644 index 00000000..1c12dba2 --- /dev/null +++ b/jslib/common/src/enums/encryptionType.ts @@ -0,0 +1,9 @@ +export enum EncryptionType { + AesCbc256_B64 = 0, + AesCbc128_HmacSha256_B64 = 1, + AesCbc256_HmacSha256_B64 = 2, + Rsa2048_OaepSha256_B64 = 3, + Rsa2048_OaepSha1_B64 = 4, + Rsa2048_OaepSha256_HmacSha256_B64 = 5, + Rsa2048_OaepSha1_HmacSha256_B64 = 6, +} diff --git a/jslib/common/src/enums/eventType.ts b/jslib/common/src/enums/eventType.ts new file mode 100644 index 00000000..236e73c0 --- /dev/null +++ b/jslib/common/src/enums/eventType.ts @@ -0,0 +1,72 @@ +export enum EventType { + User_LoggedIn = 1000, + User_ChangedPassword = 1001, + User_Updated2fa = 1002, + User_Disabled2fa = 1003, + User_Recovered2fa = 1004, + User_FailedLogIn = 1005, + User_FailedLogIn2fa = 1006, + User_ClientExportedVault = 1007, + User_UpdatedTempPassword = 1008, + User_MigratedKeyToKeyConnector = 1009, + + Cipher_Created = 1100, + Cipher_Updated = 1101, + Cipher_Deleted = 1102, + Cipher_AttachmentCreated = 1103, + Cipher_AttachmentDeleted = 1104, + Cipher_Shared = 1105, + Cipher_UpdatedCollections = 1106, + Cipher_ClientViewed = 1107, + Cipher_ClientToggledPasswordVisible = 1108, + Cipher_ClientToggledHiddenFieldVisible = 1109, + Cipher_ClientToggledCardCodeVisible = 1110, + Cipher_ClientCopiedPassword = 1111, + Cipher_ClientCopiedHiddenField = 1112, + Cipher_ClientCopiedCardCode = 1113, + Cipher_ClientAutofilled = 1114, + Cipher_SoftDeleted = 1115, + Cipher_Restored = 1116, + Cipher_ClientToggledCardNumberVisible = 1117, + + Collection_Created = 1300, + Collection_Updated = 1301, + Collection_Deleted = 1302, + + Group_Created = 1400, + Group_Updated = 1401, + Group_Deleted = 1402, + + OrganizationUser_Invited = 1500, + OrganizationUser_Confirmed = 1501, + OrganizationUser_Updated = 1502, + OrganizationUser_Removed = 1503, + OrganizationUser_UpdatedGroups = 1504, + OrganizationUser_UnlinkedSso = 1505, + OrganizationUser_ResetPassword_Enroll = 1506, + OrganizationUser_ResetPassword_Withdraw = 1507, + OrganizationUser_AdminResetPassword = 1508, + OrganizationUser_ResetSsoLink = 1509, + OrganizationUser_FirstSsoLogin = 1510, + + Organization_Updated = 1600, + Organization_PurgedVault = 1601, + // Organization_ClientExportedVault = 1602, + Organization_VaultAccessed = 1603, + Organization_EnabledSso = 1604, + Organization_DisabledSso = 1605, + Organization_EnabledKeyConnector = 1606, + Organization_DisabledKeyConnector = 1607, + + Policy_Updated = 1700, + + ProviderUser_Invited = 1800, + ProviderUser_Confirmed = 1801, + ProviderUser_Updated = 1802, + ProviderUser_Removed = 1803, + + ProviderOrganization_Created = 1900, + ProviderOrganization_Added = 1901, + ProviderOrganization_Removed = 1902, + ProviderOrganization_VaultAccessed = 1903, +} diff --git a/jslib/common/src/enums/fieldType.ts b/jslib/common/src/enums/fieldType.ts new file mode 100644 index 00000000..d6deb30e --- /dev/null +++ b/jslib/common/src/enums/fieldType.ts @@ -0,0 +1,6 @@ +export enum FieldType { + Text = 0, + Hidden = 1, + Boolean = 2, + Linked = 3, +} diff --git a/jslib/common/src/enums/fileUploadType.ts b/jslib/common/src/enums/fileUploadType.ts new file mode 100644 index 00000000..4cf654fd --- /dev/null +++ b/jslib/common/src/enums/fileUploadType.ts @@ -0,0 +1,4 @@ +export enum FileUploadType { + Direct = 0, + Azure = 1, +} diff --git a/jslib/common/src/enums/hashPurpose.ts b/jslib/common/src/enums/hashPurpose.ts new file mode 100644 index 00000000..887931b9 --- /dev/null +++ b/jslib/common/src/enums/hashPurpose.ts @@ -0,0 +1,4 @@ +export enum HashPurpose { + ServerAuthorization = 1, + LocalAuthorization = 2, +} diff --git a/jslib/common/src/enums/htmlStorageLocation.ts b/jslib/common/src/enums/htmlStorageLocation.ts new file mode 100644 index 00000000..19c84fe8 --- /dev/null +++ b/jslib/common/src/enums/htmlStorageLocation.ts @@ -0,0 +1,5 @@ +export enum HtmlStorageLocation { + Local = "local", + Memory = "memory", + Session = "session", +} diff --git a/jslib/common/src/enums/importOptions.ts b/jslib/common/src/enums/importOptions.ts new file mode 100644 index 00000000..6535b133 --- /dev/null +++ b/jslib/common/src/enums/importOptions.ts @@ -0,0 +1,74 @@ +export interface ImportOption { + id: string; + name: string; +} + +export const featuredImportOptions = [ + { id: "bitwardenjson", name: "Bitwarden (json)" }, + { id: "bitwardencsv", name: "Bitwarden (csv)" }, + { id: "chromecsv", name: "Chrome (csv)" }, + { id: "dashlanecsv", name: "Dashlane (csv)" }, + { id: "firefoxcsv", name: "Firefox (csv)" }, + { id: "keepass2xml", name: "KeePass 2 (xml)" }, + { id: "lastpasscsv", name: "LastPass (csv)" }, + { id: "safaricsv", name: "Safari and macOS (csv)" }, + { id: "1password1pux", name: "1Password (1pux)" }, +] as const; + +export const regularImportOptions = [ + { id: "keepassxcsv", name: "KeePassX (csv)" }, + { id: "1password1pif", name: "1Password (1pif)" }, + { id: "1passwordwincsv", name: "1Password 6 and 7 Windows (csv)" }, + { id: "1passwordmaccsv", name: "1Password 6 and 7 Mac (csv)" }, + { id: "dashlanejson", name: "Dashlane (json)" }, + { id: "roboformcsv", name: "RoboForm (csv)" }, + { id: "keepercsv", name: "Keeper (csv)" }, + // Temporarily remove this option for the Feb release + // { id: "keeperjson", name: "Keeper (json)" }, + { id: "enpasscsv", name: "Enpass (csv)" }, + { id: "enpassjson", name: "Enpass (json)" }, + { id: "safeincloudxml", name: "SafeInCloud (xml)" }, + { id: "pwsafexml", name: "Password Safe (xml)" }, + { id: "stickypasswordxml", name: "Sticky Password (xml)" }, + { id: "msecurecsv", name: "mSecure (csv)" }, + { id: "truekeycsv", name: "True Key (csv)" }, + { id: "passwordbossjson", name: "Password Boss (json)" }, + { id: "zohovaultcsv", name: "Zoho Vault (csv)" }, + { id: "splashidcsv", name: "SplashID (csv)" }, + { id: "passworddragonxml", name: "Password Dragon (xml)" }, + { id: "padlockcsv", name: "Padlock (csv)" }, + { id: "passboltcsv", name: "Passbolt (csv)" }, + { id: "clipperzhtml", name: "Clipperz (html)" }, + { id: "aviracsv", name: "Avira (csv)" }, + { id: "saferpasscsv", name: "SaferPass (csv)" }, + { id: "upmcsv", name: "Universal Password Manager (csv)" }, + { id: "ascendocsv", name: "Ascendo DataVault (csv)" }, + { id: "meldiumcsv", name: "Meldium (csv)" }, + { id: "passkeepcsv", name: "PassKeep (csv)" }, + { id: "operacsv", name: "Opera (csv)" }, + { id: "vivaldicsv", name: "Vivaldi (csv)" }, + { id: "gnomejson", name: "GNOME Passwords and Keys/Seahorse (json)" }, + { id: "blurcsv", name: "Blur (csv)" }, + { id: "passwordagentcsv", name: "Password Agent (csv)" }, + { id: "passpackcsv", name: "Passpack (csv)" }, + { id: "passmanjson", name: "Passman (json)" }, + { id: "avastcsv", name: "Avast Passwords (csv)" }, + { id: "avastjson", name: "Avast Passwords (json)" }, + { id: "fsecurefsk", name: "F-Secure KEY (fsk)" }, + { id: "kasperskytxt", name: "Kaspersky Password Manager (txt)" }, + { id: "remembearcsv", name: "RememBear (csv)" }, + { id: "passwordwallettxt", name: "PasswordWallet (txt)" }, + { id: "mykicsv", name: "Myki (csv)" }, + { id: "securesafecsv", name: "SecureSafe (csv)" }, + { id: "logmeoncecsv", name: "LogMeOnce (csv)" }, + { id: "blackberrycsv", name: "BlackBerry Password Keeper (csv)" }, + { id: "buttercupcsv", name: "Buttercup (csv)" }, + { id: "codebookcsv", name: "Codebook (csv)" }, + { id: "encryptrcsv", name: "Encryptr (csv)" }, + { id: "yoticsv", name: "Yoti (csv)" }, + { id: "nordpasscsv", name: "Nordpass (csv)" }, +] as const; + +export type ImportType = + | typeof featuredImportOptions[number]["id"] + | typeof regularImportOptions[number]["id"]; diff --git a/jslib/common/src/enums/kdfType.ts b/jslib/common/src/enums/kdfType.ts new file mode 100644 index 00000000..cc7fa7e0 --- /dev/null +++ b/jslib/common/src/enums/kdfType.ts @@ -0,0 +1,7 @@ +export enum KdfType { + PBKDF2_SHA256 = 0, +} + +export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256; +export const DEFAULT_KDF_ITERATIONS = 100000; +export const SEND_KDF_ITERATIONS = 100000; diff --git a/jslib/common/src/enums/keySuffixOptions.ts b/jslib/common/src/enums/keySuffixOptions.ts new file mode 100644 index 00000000..2ae98d8e --- /dev/null +++ b/jslib/common/src/enums/keySuffixOptions.ts @@ -0,0 +1,4 @@ +export enum KeySuffixOptions { + Auto = "auto", + Biometric = "biometric", +} diff --git a/jslib/common/src/enums/linkedIdType.ts b/jslib/common/src/enums/linkedIdType.ts new file mode 100644 index 00000000..c38ebc1c --- /dev/null +++ b/jslib/common/src/enums/linkedIdType.ts @@ -0,0 +1,40 @@ +export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; + +// LoginView +export enum LoginLinkedId { + Username = 100, + Password = 101, +} + +// CardView +export enum CardLinkedId { + CardholderName = 300, + ExpMonth = 301, + ExpYear = 302, + Code = 303, + Brand = 304, + Number = 305, +} + +// IdentityView +export enum IdentityLinkedId { + Title = 400, + MiddleName = 401, + Address1 = 402, + Address2 = 403, + Address3 = 404, + City = 405, + State = 406, + PostalCode = 407, + Country = 408, + Company = 409, + Email = 410, + Phone = 411, + Ssn = 412, + Username = 413, + PassportNumber = 414, + LicenseNumber = 415, + FirstName = 416, + LastName = 417, + FullName = 418, +} diff --git a/jslib/common/src/enums/logLevelType.ts b/jslib/common/src/enums/logLevelType.ts new file mode 100644 index 00000000..709871dd --- /dev/null +++ b/jslib/common/src/enums/logLevelType.ts @@ -0,0 +1,6 @@ +export enum LogLevelType { + Debug, + Info, + Warning, + Error, +} diff --git a/jslib/common/src/enums/notificationType.ts b/jslib/common/src/enums/notificationType.ts new file mode 100644 index 00000000..77ebde01 --- /dev/null +++ b/jslib/common/src/enums/notificationType.ts @@ -0,0 +1,20 @@ +export enum NotificationType { + SyncCipherUpdate = 0, + SyncCipherCreate = 1, + SyncLoginDelete = 2, + SyncFolderDelete = 3, + SyncCiphers = 4, + + SyncVault = 5, + SyncOrgKeys = 6, + SyncFolderCreate = 7, + SyncFolderUpdate = 8, + SyncCipherDelete = 9, + SyncSettings = 10, + + LogOut = 11, + + SyncSendCreate = 12, + SyncSendUpdate = 13, + SyncSendDelete = 14, +} diff --git a/jslib/common/src/enums/organizationUserStatusType.ts b/jslib/common/src/enums/organizationUserStatusType.ts new file mode 100644 index 00000000..33422180 --- /dev/null +++ b/jslib/common/src/enums/organizationUserStatusType.ts @@ -0,0 +1,5 @@ +export enum OrganizationUserStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, +} diff --git a/jslib/common/src/enums/organizationUserType.ts b/jslib/common/src/enums/organizationUserType.ts new file mode 100644 index 00000000..950fbaae --- /dev/null +++ b/jslib/common/src/enums/organizationUserType.ts @@ -0,0 +1,7 @@ +export enum OrganizationUserType { + Owner = 0, + Admin = 1, + User = 2, + Manager = 3, + Custom = 4, +} diff --git a/jslib/common/src/enums/paymentMethodType.ts b/jslib/common/src/enums/paymentMethodType.ts new file mode 100644 index 00000000..701bd886 --- /dev/null +++ b/jslib/common/src/enums/paymentMethodType.ts @@ -0,0 +1,11 @@ +export enum PaymentMethodType { + Card = 0, + BankAccount = 1, + PayPal = 2, + BitPay = 3, + Credit = 4, + WireTransfer = 5, + AppleInApp = 6, + GoogleInApp = 7, + Check = 8, +} diff --git a/jslib/common/src/enums/permissions.ts b/jslib/common/src/enums/permissions.ts new file mode 100644 index 00000000..46a74455 --- /dev/null +++ b/jslib/common/src/enums/permissions.ts @@ -0,0 +1,27 @@ +export enum Permissions { + AccessEventLogs, + AccessImportExport, + AccessReports, + /** + * @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and + * `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + ManageAllCollections, + /** + * @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and + * `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + ManageAssignedCollections, + ManageGroups, + ManageOrganization, + ManagePolicies, + ManageProvider, + ManageUsers, + ManageUsersPassword, + CreateNewCollections, + EditAnyCollection, + DeleteAnyCollection, + EditAssignedCollections, + DeleteAssignedCollections, + ManageSso, +} diff --git a/jslib/common/src/enums/planSponsorshipType.ts b/jslib/common/src/enums/planSponsorshipType.ts new file mode 100644 index 00000000..3b4c0046 --- /dev/null +++ b/jslib/common/src/enums/planSponsorshipType.ts @@ -0,0 +1,3 @@ +export enum PlanSponsorshipType { + FamiliesForEnterprise = 0, +} diff --git a/jslib/common/src/enums/planType.ts b/jslib/common/src/enums/planType.ts new file mode 100644 index 00000000..f4c89d2c --- /dev/null +++ b/jslib/common/src/enums/planType.ts @@ -0,0 +1,14 @@ +export enum PlanType { + Free = 0, + FamiliesAnnually2019 = 1, + TeamsMonthly2019 = 2, + TeamsAnnually2019 = 3, + EnterpriseMonthly2019 = 4, + EnterpriseAnnually2019 = 5, + Custom = 6, + FamiliesAnnually = 7, + TeamsMonthly = 8, + TeamsAnnually = 9, + EnterpriseMonthly = 10, + EnterpriseAnnually = 11, +} diff --git a/jslib/common/src/enums/policyType.ts b/jslib/common/src/enums/policyType.ts new file mode 100644 index 00000000..02dce41e --- /dev/null +++ b/jslib/common/src/enums/policyType.ts @@ -0,0 +1,13 @@ +export enum PolicyType { + TwoFactorAuthentication = 0, // Requires users to have 2fa enabled + MasterPassword = 1, // Sets minimum requirements for master password complexity + PasswordGenerator = 2, // Sets minimum requirements/default type for generated passwords/passphrases + SingleOrg = 3, // Allows users to only be apart of one organization + RequireSso = 4, // Requires users to authenticate with SSO + PersonalOwnership = 5, // Disables personal vault ownership for adding/cloning items + DisableSend = 6, // Disables the ability to create and edit Bitwarden Sends + SendOptions = 7, // Sets restrictions or defaults for Bitwarden Sends + ResetPassword = 8, // Allows orgs to use reset password : also can enable auto-enrollment during invite flow + MaximumVaultTimeout = 9, // Sets the maximum allowed vault timeout + DisablePersonalVaultExport = 10, // Disable personal vault export +} diff --git a/jslib/common/src/enums/productType.ts b/jslib/common/src/enums/productType.ts new file mode 100644 index 00000000..50b836d1 --- /dev/null +++ b/jslib/common/src/enums/productType.ts @@ -0,0 +1,6 @@ +export enum ProductType { + Free = 0, + Families = 1, + Teams = 2, + Enterprise = 3, +} diff --git a/jslib/common/src/enums/providerUserStatusType.ts b/jslib/common/src/enums/providerUserStatusType.ts new file mode 100644 index 00000000..3b845551 --- /dev/null +++ b/jslib/common/src/enums/providerUserStatusType.ts @@ -0,0 +1,5 @@ +export enum ProviderUserStatusType { + Invited = 0, + Accepted = 1, + Confirmed = 2, +} diff --git a/jslib/common/src/enums/providerUserType.ts b/jslib/common/src/enums/providerUserType.ts new file mode 100644 index 00000000..00490adc --- /dev/null +++ b/jslib/common/src/enums/providerUserType.ts @@ -0,0 +1,4 @@ +export enum ProviderUserType { + ProviderAdmin = 0, + ServiceUser = 1, +} diff --git a/jslib/common/src/enums/secureNoteType.ts b/jslib/common/src/enums/secureNoteType.ts new file mode 100644 index 00000000..8015236d --- /dev/null +++ b/jslib/common/src/enums/secureNoteType.ts @@ -0,0 +1,3 @@ +export enum SecureNoteType { + Generic = 0, +} diff --git a/jslib/common/src/enums/sendType.ts b/jslib/common/src/enums/sendType.ts new file mode 100644 index 00000000..487930c9 --- /dev/null +++ b/jslib/common/src/enums/sendType.ts @@ -0,0 +1,4 @@ +export enum SendType { + Text = 0, + File = 1, +} diff --git a/jslib/common/src/enums/ssoEnums.ts b/jslib/common/src/enums/ssoEnums.ts new file mode 100644 index 00000000..d9be7274 --- /dev/null +++ b/jslib/common/src/enums/ssoEnums.ts @@ -0,0 +1,33 @@ +export enum SsoType { + None = 0, + OpenIdConnect = 1, + Saml2 = 2, +} + +export enum OpenIdConnectRedirectBehavior { + RedirectGet = 0, + FormPost = 1, +} + +export enum Saml2BindingType { + HttpRedirect = 1, + HttpPost = 2, +} + +export enum Saml2NameIdFormat { + NotConfigured = 0, + Unspecified = 1, + EmailAddress = 2, + X509SubjectName = 3, + WindowsDomainQualifiedName = 4, + KerberosPrincipalName = 5, + EntityIdentifier = 6, + Persistent = 7, + Transient = 8, +} + +export enum Saml2SigningBehavior { + IfIdpWantAuthnRequestsSigned = 0, + Always = 1, + Never = 3, +} diff --git a/jslib/common/src/enums/stateVersion.ts b/jslib/common/src/enums/stateVersion.ts new file mode 100644 index 00000000..5aeb02e5 --- /dev/null +++ b/jslib/common/src/enums/stateVersion.ts @@ -0,0 +1,7 @@ +export enum StateVersion { + One = 1, // Original flat key/value pair store + Two = 2, // Move to a typed State object + Three = 3, // Fix migration of users' premium status + Four = 4, // Fix 'Never Lock' option by removing stale data + Latest = Four, +} diff --git a/jslib/common/src/enums/storageLocation.ts b/jslib/common/src/enums/storageLocation.ts new file mode 100644 index 00000000..46d50d1d --- /dev/null +++ b/jslib/common/src/enums/storageLocation.ts @@ -0,0 +1,5 @@ +export enum StorageLocation { + Both = "both", + Disk = "disk", + Memory = "memory", +} diff --git a/jslib/common/src/enums/themeType.ts b/jslib/common/src/enums/themeType.ts new file mode 100644 index 00000000..8afca770 --- /dev/null +++ b/jslib/common/src/enums/themeType.ts @@ -0,0 +1,7 @@ +export enum ThemeType { + System = "system", + Light = "light", + Dark = "dark", + Nord = "nord", + SolarizedDark = "solarizedDark", +} diff --git a/jslib/common/src/enums/transactionType.ts b/jslib/common/src/enums/transactionType.ts new file mode 100644 index 00000000..34731a2e --- /dev/null +++ b/jslib/common/src/enums/transactionType.ts @@ -0,0 +1,7 @@ +export enum TransactionType { + Charge = 0, + Credit = 1, + PromotionalCredit = 2, + ReferralCredit = 3, + Refund = 4, +} diff --git a/jslib/common/src/enums/twoFactorProviderType.ts b/jslib/common/src/enums/twoFactorProviderType.ts new file mode 100644 index 00000000..a1708032 --- /dev/null +++ b/jslib/common/src/enums/twoFactorProviderType.ts @@ -0,0 +1,10 @@ +export enum TwoFactorProviderType { + Authenticator = 0, + Email = 1, + Duo = 2, + Yubikey = 3, + U2f = 4, + Remember = 5, + OrganizationDuo = 6, + WebAuthn = 7, +} diff --git a/jslib/common/src/enums/uriMatchType.ts b/jslib/common/src/enums/uriMatchType.ts new file mode 100644 index 00000000..4a4193ba --- /dev/null +++ b/jslib/common/src/enums/uriMatchType.ts @@ -0,0 +1,8 @@ +export enum UriMatchType { + Domain = 0, + Host = 1, + StartsWith = 2, + Exact = 3, + RegularExpression = 4, + Never = 5, +} diff --git a/jslib/common/src/enums/verificationType.ts b/jslib/common/src/enums/verificationType.ts new file mode 100644 index 00000000..76a51ab7 --- /dev/null +++ b/jslib/common/src/enums/verificationType.ts @@ -0,0 +1,4 @@ +export enum VerificationType { + MasterPassword = 0, + OTP = 1, +} diff --git a/jslib/common/src/factories/accountFactory.ts b/jslib/common/src/factories/accountFactory.ts new file mode 100644 index 00000000..1fe5aee3 --- /dev/null +++ b/jslib/common/src/factories/accountFactory.ts @@ -0,0 +1,13 @@ +import { Account } from "../models/domain/account"; + +export class AccountFactory { + private accountConstructor: new (init: Partial) => T; + + constructor(accountConstructor: new (init: Partial) => T) { + this.accountConstructor = accountConstructor; + } + + create(args: Partial) { + return new this.accountConstructor(args); + } +} diff --git a/jslib/common/src/factories/globalStateFactory.ts b/jslib/common/src/factories/globalStateFactory.ts new file mode 100644 index 00000000..a2c25c46 --- /dev/null +++ b/jslib/common/src/factories/globalStateFactory.ts @@ -0,0 +1,13 @@ +import { GlobalState } from "../models/domain/globalState"; + +export class GlobalStateFactory { + private globalStateConstructor: new (init: Partial) => T; + + constructor(globalStateConstructor: new (init: Partial) => T) { + this.globalStateConstructor = globalStateConstructor; + } + + create(args?: Partial) { + return new this.globalStateConstructor(args); + } +} diff --git a/jslib/common/src/factories/stateFactory.ts b/jslib/common/src/factories/stateFactory.ts new file mode 100644 index 00000000..203ae672 --- /dev/null +++ b/jslib/common/src/factories/stateFactory.ts @@ -0,0 +1,29 @@ +import { Account } from "../models/domain/account"; +import { GlobalState } from "../models/domain/globalState"; + +import { AccountFactory } from "./accountFactory"; +import { GlobalStateFactory } from "./globalStateFactory"; + +export class StateFactory< + TGlobal extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + private globalStateFactory: GlobalStateFactory; + private accountFactory: AccountFactory; + + constructor( + globalStateConstructor: new (init: Partial) => TGlobal, + accountConstructor: new (init: Partial) => TAccount + ) { + this.globalStateFactory = new GlobalStateFactory(globalStateConstructor); + this.accountFactory = new AccountFactory(accountConstructor); + } + + createGlobal(args: Partial): TGlobal { + return this.globalStateFactory.create(args); + } + + createAccount(args: Partial): TAccount { + return this.accountFactory.create(args); + } +} diff --git a/jslib/common/src/globals.d.ts b/jslib/common/src/globals.d.ts new file mode 100644 index 00000000..4859a086 --- /dev/null +++ b/jslib/common/src/globals.d.ts @@ -0,0 +1,2 @@ +declare function escape(s: string): string; +declare function unescape(s: string): string; diff --git a/jslib/common/src/importers/ascendoCsvImporter.ts b/jslib/common/src/importers/ascendoCsvImporter.ts new file mode 100644 index 00000000..75191a1c --- /dev/null +++ b/jslib/common/src/importers/ascendoCsvImporter.ts @@ -0,0 +1,59 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AscendoCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 2) { + return; + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 1]); + cipher.name = this.getValueOrDefault(value[0], "--"); + + if (value.length > 2 && value.length % 2 === 0) { + for (let i = 0; i < value.length - 2; i += 2) { + const val: string = value[i + 2]; + const field: string = value[i + 1]; + if (this.isNullOrWhitespace(val) || this.isNullOrWhitespace(field)) { + continue; + } + + const fieldLower = field.toLowerCase(); + if (cipher.login.password == null && this.passwordFieldNames.indexOf(fieldLower) > -1) { + cipher.login.password = this.getValueOrDefault(val); + } else if ( + cipher.login.username == null && + this.usernameFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.username = this.getValueOrDefault(val); + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.uris = this.makeUriArray(val); + } else { + this.processKvp(cipher, field, val); + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/avastCsvImporter.ts b/jslib/common/src/importers/avastCsvImporter.ts new file mode 100644 index 00000000..9bf10639 --- /dev/null +++ b/jslib/common/src/importers/avastCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AvastCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name); + cipher.login.uris = this.makeUriArray(value.web); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.login); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/avastJsonImporter.ts b/jslib/common/src/importers/avastJsonImporter.ts new file mode 100644 index 00000000..0d424a71 --- /dev/null +++ b/jslib/common/src/importers/avastJsonImporter.ts @@ -0,0 +1,68 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AvastJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + if (results.logins != null) { + results.logins.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.custName); + cipher.notes = this.getValueOrDefault(value.note); + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.password = this.getValueOrDefault(value.pwd); + cipher.login.username = this.getValueOrDefault(value.loginName); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (results.notes != null) { + results.notes.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = this.getValueOrDefault(value.label); + cipher.notes = this.getValueOrDefault(value.text); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (results.cards != null) { + results.cards.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.Card; + cipher.name = this.getValueOrDefault(value.custName); + cipher.notes = this.getValueOrDefault(value.note); + cipher.card.cardholderName = this.getValueOrDefault(value.holderName); + cipher.card.number = this.getValueOrDefault(value.cardNumber); + cipher.card.code = this.getValueOrDefault(value.cvv); + cipher.card.brand = this.getCardBrand(cipher.card.number); + if (value.expirationDate != null) { + if (value.expirationDate.month != null) { + cipher.card.expMonth = value.expirationDate.month + ""; + } + if (value.expirationDate.year != null) { + cipher.card.expYear = value.expirationDate.year + ""; + } + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/aviraCsvImporter.ts b/jslib/common/src/importers/aviraCsvImporter.ts new file mode 100644 index 00000000..f4c578b0 --- /dev/null +++ b/jslib/common/src/importers/aviraCsvImporter.ts @@ -0,0 +1,41 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class AviraCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault( + value.name, + this.getValueOrDefault(this.nameFromUrl(value.website), "--") + ); + cipher.login.uris = this.makeUriArray(value.website); + cipher.login.password = this.getValueOrDefault(value.password); + + if ( + this.isNullOrWhitespace(value.username) && + !this.isNullOrWhitespace(value.secondary_username) + ) { + cipher.login.username = value.secondary_username; + } else { + cipher.login.username = this.getValueOrDefault(value.username); + cipher.notes = this.getValueOrDefault(value.secondary_username); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/baseImporter.ts b/jslib/common/src/importers/baseImporter.ts new file mode 100644 index 00000000..122b3472 --- /dev/null +++ b/jslib/common/src/importers/baseImporter.ts @@ -0,0 +1,466 @@ +import * as papa from "papaparse"; + +import { LogService } from "../abstractions/log.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { Utils } from "../misc/utils"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FieldView } from "../models/view/fieldView"; +import { FolderView } from "../models/view/folderView"; +import { LoginUriView } from "../models/view/loginUriView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; +import { ConsoleLogService } from "../services/consoleLog.service"; + +export abstract class BaseImporter { + organizationId: string = null; + + protected logService: LogService = new ConsoleLogService(false); + + protected newLineRegex = /(?:\r\n|\r|\n)/; + + protected passwordFieldNames = [ + "password", + "pass word", + "passphrase", + "pass phrase", + "pass", + "code", + "code word", + "codeword", + "secret", + "secret word", + "personpwd", + "key", + "keyword", + "key word", + "keyphrase", + "key phrase", + "form_pw", + "wppassword", + "pin", + "pwd", + "pw", + "pword", + "passwd", + "p", + "serial", + "serial#", + "license key", + "reg #", + + // Non-English names + "passwort", + ]; + + protected usernameFieldNames = [ + "user", + "name", + "user name", + "username", + "login name", + "email", + "e-mail", + "id", + "userid", + "user id", + "login", + "form_loginname", + "wpname", + "mail", + "loginid", + "login id", + "log", + "personlogin", + "first name", + "last name", + "card#", + "account #", + "member", + "member #", + + // Non-English names + "nom", + "benutzername", + ]; + + protected notesFieldNames = [ + "note", + "notes", + "comment", + "comments", + "memo", + "description", + "free form", + "freeform", + "free text", + "freetext", + "free", + + // Non-English names + "kommentar", + ]; + + protected uriFieldNames: string[] = [ + "url", + "hyper link", + "hyperlink", + "link", + "host", + "hostname", + "host name", + "server", + "address", + "hyper ref", + "href", + "web", + "website", + "web site", + "site", + "web-site", + "uri", + + // Non-English names + "ort", + "adresse", + ]; + + protected parseCsvOptions = { + encoding: "UTF-8", + skipEmptyLines: false, + }; + + protected get organization() { + return this.organizationId != null; + } + + protected parseXml(data: string): Document { + const parser = new DOMParser(); + const doc = parser.parseFromString(data, "application/xml"); + return doc != null && doc.querySelector("parsererror") == null ? doc : null; + } + + protected parseCsv(data: string, header: boolean, options: any = {}): any[] { + const parseOptions: papa.ParseConfig = Object.assign( + { header: header }, + this.parseCsvOptions, + options + ); + data = this.splitNewLine(data).join("\n").trim(); + const result = papa.parse(data, parseOptions); + if (result.errors != null && result.errors.length > 0) { + result.errors.forEach((e) => { + if (e.row != null) { + this.logService.warning("Error parsing row " + e.row + ": " + e.message); + } + }); + } + return result.data && result.data.length > 0 ? result.data : null; + } + + protected parseSingleRowCsv(rowData: string) { + if (this.isNullOrWhitespace(rowData)) { + return null; + } + const parsedRow = this.parseCsv(rowData, false); + if (parsedRow != null && parsedRow.length > 0 && parsedRow[0].length > 0) { + return parsedRow[0]; + } + return null; + } + + protected makeUriArray(uri: string | string[]): LoginUriView[] { + if (uri == null) { + return null; + } + + if (typeof uri === "string") { + const loginUri = new LoginUriView(); + loginUri.uri = this.fixUri(uri); + if (this.isNullOrWhitespace(loginUri.uri)) { + return null; + } + loginUri.match = null; + return [loginUri]; + } + + if (uri.length > 0) { + const returnArr: LoginUriView[] = []; + uri.forEach((u) => { + const loginUri = new LoginUriView(); + loginUri.uri = this.fixUri(u); + if (this.isNullOrWhitespace(loginUri.uri)) { + return; + } + loginUri.match = null; + returnArr.push(loginUri); + }); + return returnArr.length === 0 ? null : returnArr; + } + + return null; + } + + protected fixUri(uri: string) { + if (uri == null) { + return null; + } + uri = uri.trim(); + if (uri.indexOf("://") === -1 && uri.indexOf(".") >= 0) { + uri = "http://" + uri; + } + if (uri.length > 1000) { + return uri.substring(0, 1000); + } + return uri; + } + + protected nameFromUrl(url: string) { + const hostname = Utils.getHostname(url); + if (this.isNullOrWhitespace(hostname)) { + return null; + } + return hostname.startsWith("www.") ? hostname.replace("www.", "") : hostname; + } + + protected isNullOrWhitespace(str: string): boolean { + return Utils.isNullOrWhitespace(str); + } + + protected getValueOrDefault(str: string, defaultValue: string = null): string { + if (this.isNullOrWhitespace(str)) { + return defaultValue; + } + return str; + } + + protected splitNewLine(str: string): string[] { + return str.split(this.newLineRegex); + } + + // ref https://stackoverflow.com/a/5911300 + protected getCardBrand(cardNum: string) { + if (this.isNullOrWhitespace(cardNum)) { + return null; + } + + // Visa + let re = new RegExp("^4"); + if (cardNum.match(re) != null) { + return "Visa"; + } + + // Mastercard + // Updated for Mastercard 2017 BINs expansion + if ( + /^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test( + cardNum + ) + ) { + return "Mastercard"; + } + + // AMEX + re = new RegExp("^3[47]"); + if (cardNum.match(re) != null) { + return "Amex"; + } + + // Discover + re = new RegExp( + "^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)" + ); + if (cardNum.match(re) != null) { + return "Discover"; + } + + // Diners + re = new RegExp("^36"); + if (cardNum.match(re) != null) { + return "Diners Club"; + } + + // Diners - Carte Blanche + re = new RegExp("^30[0-5]"); + if (cardNum.match(re) != null) { + return "Diners Club"; + } + + // JCB + re = new RegExp("^35(2[89]|[3-8][0-9])"); + if (cardNum.match(re) != null) { + return "JCB"; + } + + // Visa Electron + re = new RegExp("^(4026|417500|4508|4844|491(3|7))"); + if (cardNum.match(re) != null) { + return "Visa"; + } + + return null; + } + + protected setCardExpiration(cipher: CipherView, expiration: string): boolean { + if (!this.isNullOrWhitespace(expiration)) { + expiration = expiration.replace(/\s/g, ""); + const parts = expiration.split("/"); + if (parts.length === 2) { + let month: string = null; + let year: string = null; + if (parts[0].length === 1 || parts[0].length === 2) { + month = parts[0]; + if (month.length === 2 && month[0] === "0") { + month = month.substr(1, 1); + } + } + if (parts[1].length === 2 || parts[1].length === 4) { + year = month.length === 2 ? "20" + parts[1] : parts[1]; + } + if (month != null && year != null) { + cipher.card.expMonth = month; + cipher.card.expYear = year; + return true; + } + } + } + return false; + } + + protected moveFoldersToCollections(result: ImportResult) { + result.folderRelationships.forEach((r) => result.collectionRelationships.push(r)); + result.collections = result.folders.map((f) => { + const collection = new CollectionView(); + collection.name = f.name; + return collection; + }); + result.folderRelationships = []; + result.folders = []; + } + + protected querySelectorDirectChild(parentEl: Element, query: string) { + const els = this.querySelectorAllDirectChild(parentEl, query); + return els.length === 0 ? null : els[0]; + } + + protected querySelectorAllDirectChild(parentEl: Element, query: string) { + return Array.from(parentEl.querySelectorAll(query)).filter((el) => el.parentNode === parentEl); + } + + protected initLoginCipher() { + const cipher = new CipherView(); + cipher.favorite = false; + cipher.notes = ""; + cipher.fields = []; + cipher.login = new LoginView(); + cipher.type = CipherType.Login; + return cipher; + } + + protected cleanupCipher(cipher: CipherView) { + if (cipher == null) { + return; + } + if (cipher.type !== CipherType.Login) { + cipher.login = null; + } + if (this.isNullOrWhitespace(cipher.name)) { + cipher.name = "--"; + } + if (this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = null; + } else { + cipher.notes = cipher.notes.trim(); + } + if (cipher.fields != null && cipher.fields.length === 0) { + cipher.fields = null; + } + } + + protected processKvp( + cipher: CipherView, + key: string, + value: string, + type: FieldType = FieldType.Text + ) { + if (this.isNullOrWhitespace(value)) { + return; + } + if (this.isNullOrWhitespace(key)) { + key = ""; + } + if (value.length > 200 || value.trim().search(this.newLineRegex) > -1) { + if (cipher.notes == null) { + cipher.notes = ""; + } + cipher.notes += key + ": " + this.splitNewLine(value).join("\n") + "\n"; + } else { + if (cipher.fields == null) { + cipher.fields = []; + } + const field = new FieldView(); + field.type = type; + field.name = key; + field.value = value; + cipher.fields.push(field); + } + } + + protected processFolder(result: ImportResult, folderName: string) { + let folderIndex = result.folders.length; + const hasFolder = !this.isNullOrWhitespace(folderName); + let addFolder = hasFolder; + + if (hasFolder) { + for (let i = 0; i < result.folders.length; i++) { + if (result.folders[i].name === folderName) { + addFolder = false; + folderIndex = i; + break; + } + } + } + + if (addFolder) { + const f = new FolderView(); + f.name = folderName; + result.folders.push(f); + } + if (hasFolder) { + result.folderRelationships.push([result.ciphers.length, folderIndex]); + } + } + + protected convertToNoteIfNeeded(cipher: CipherView) { + if ( + cipher.type === CipherType.Login && + this.isNullOrWhitespace(cipher.login.username) && + this.isNullOrWhitespace(cipher.login.password) && + (cipher.login.uris == null || cipher.login.uris.length === 0) + ) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } + } + + protected processFullName(cipher: CipherView, fullName: string) { + if (this.isNullOrWhitespace(fullName)) { + return; + } + + const nameParts = fullName.split(" "); + if (nameParts.length > 0) { + cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); + } + if (nameParts.length === 2) { + cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); + } else if (nameParts.length >= 3) { + cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); + cipher.identity.lastName = nameParts.slice(2, nameParts.length).join(" "); + } + } +} diff --git a/jslib/common/src/importers/bitwardenCsvImporter.ts b/jslib/common/src/importers/bitwardenCsvImporter.ts new file mode 100644 index 00000000..cc5419c3 --- /dev/null +++ b/jslib/common/src/importers/bitwardenCsvImporter.ts @@ -0,0 +1,120 @@ +import { CipherRepromptType } from "../enums/cipherRepromptType"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FieldView } from "../models/view/fieldView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BitwardenCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.organization && !this.isNullOrWhitespace(value.collections)) { + const collections = (value.collections as string).split(","); + collections.forEach((col) => { + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === col) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = col; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else if (!this.organization) { + this.processFolder(result, value.folder); + } + + const cipher = new CipherView(); + cipher.favorite = + !this.organization && this.getValueOrDefault(value.favorite, "0") !== "0" ? true : false; + cipher.type = CipherType.Login; + cipher.notes = this.getValueOrDefault(value.notes); + cipher.name = this.getValueOrDefault(value.name, "--"); + try { + cipher.reprompt = parseInt( + this.getValueOrDefault(value.reprompt, CipherRepromptType.None.toString()), + 10 + ); + } catch (e) { + // eslint-disable-next-line + console.error("Unable to parse reprompt value", e); + cipher.reprompt = CipherRepromptType.None; + } + + if (!this.isNullOrWhitespace(value.fields)) { + const fields = this.splitNewLine(value.fields); + for (let i = 0; i < fields.length; i++) { + if (this.isNullOrWhitespace(fields[i])) { + continue; + } + + const delimPosition = fields[i].lastIndexOf(": "); + if (delimPosition === -1) { + continue; + } + + if (cipher.fields == null) { + cipher.fields = []; + } + + const field = new FieldView(); + field.name = fields[i].substr(0, delimPosition); + field.value = null; + field.type = FieldType.Text; + if (fields[i].length > delimPosition + 2) { + field.value = fields[i].substr(delimPosition + 2); + } + cipher.fields.push(field); + } + } + + const valueType = value.type != null ? value.type.toLowerCase() : null; + switch (valueType) { + case "note": + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + default: { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.totp = this.getValueOrDefault(value.login_totp || value.totp); + cipher.login.username = this.getValueOrDefault(value.login_username || value.username); + cipher.login.password = this.getValueOrDefault(value.login_password || value.password); + const uris = this.parseSingleRowCsv(value.login_uri || value.uri); + cipher.login.uris = this.makeUriArray(uris); + break; + } + } + + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/bitwardenJsonImporter.ts b/jslib/common/src/importers/bitwardenJsonImporter.ts new file mode 100644 index 00000000..6caf185e --- /dev/null +++ b/jslib/common/src/importers/bitwardenJsonImporter.ts @@ -0,0 +1,179 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { EncString } from "../models/domain/encString"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherWithIds } from "../models/export/cipherWithIds"; +import { CollectionWithId } from "../models/export/collectionWithId"; +import { FolderWithId } from "../models/export/folderWithId"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BitwardenJsonImporter extends BaseImporter implements Importer { + private results: any; + private result: ImportResult; + + constructor(protected cryptoService: CryptoService, protected i18nService: I18nService) { + super(); + } + + async parse(data: string): Promise { + this.result = new ImportResult(); + this.results = JSON.parse(data); + if (this.results == null || this.results.items == null) { + if (this.results?.passwordProtected) { + this.result.success = false; + this.result.missingPassword = true; + this.result.errorMessage = this.i18nService.t("importPasswordRequired"); + return this.result; + } + + this.result.success = false; + return this.result; + } + + if (this.results.encrypted) { + await this.parseEncrypted(); + } else { + this.parseDecrypted(); + } + + return this.result; + } + + private async parseEncrypted() { + if (this.results.encKeyValidation_DO_NOT_EDIT != null) { + const orgKey = await this.cryptoService.getOrgKey(this.organizationId); + const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT); + const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8( + encKeyValidation, + orgKey + ); + if (encKeyValidationDecrypt === null) { + this.result.success = false; + this.result.errorMessage = this.i18nService.t("importEncKeyError"); + return; + } + } + + const groupingsMap = new Map(); + + if (this.organization && this.results.collections != null) { + for (const c of this.results.collections as CollectionWithId[]) { + const collection = CollectionWithId.toDomain(c); + if (collection != null) { + collection.id = null; + collection.organizationId = this.organizationId; + const view = await collection.decrypt(); + groupingsMap.set(c.id, this.result.collections.length); + this.result.collections.push(view); + } + } + } else if (!this.organization && this.results.folders != null) { + for (const f of this.results.folders as FolderWithId[]) { + const folder = FolderWithId.toDomain(f); + if (folder != null) { + folder.id = null; + const view = await folder.decrypt(); + groupingsMap.set(f.id, this.result.folders.length); + this.result.folders.push(view); + } + } + } + + for (const c of this.results.items as CipherWithIds[]) { + const cipher = CipherWithIds.toDomain(c); + // reset ids incase they were set for some reason + cipher.id = null; + cipher.folderId = null; + cipher.organizationId = this.organizationId; + cipher.collectionIds = null; + + // make sure password history is limited + if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) { + cipher.passwordHistory = cipher.passwordHistory.slice(0, 5); + } + + if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) { + this.result.folderRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(c.folderId), + ]); + } else if (this.organization && c.collectionIds != null) { + c.collectionIds.forEach((cId) => { + if (groupingsMap.has(cId)) { + this.result.collectionRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(cId), + ]); + } + }); + } + + const view = await cipher.decrypt(); + this.cleanupCipher(view); + this.result.ciphers.push(view); + } + + this.result.success = true; + } + + private parseDecrypted() { + const groupingsMap = new Map(); + if (this.organization && this.results.collections != null) { + this.results.collections.forEach((c: CollectionWithId) => { + const collection = CollectionWithId.toView(c); + if (collection != null) { + collection.id = null; + collection.organizationId = null; + groupingsMap.set(c.id, this.result.collections.length); + this.result.collections.push(collection); + } + }); + } else if (!this.organization && this.results.folders != null) { + this.results.folders.forEach((f: FolderWithId) => { + const folder = FolderWithId.toView(f); + if (folder != null) { + folder.id = null; + groupingsMap.set(f.id, this.result.folders.length); + this.result.folders.push(folder); + } + }); + } + + this.results.items.forEach((c: CipherWithIds) => { + const cipher = CipherWithIds.toView(c); + // reset ids incase they were set for some reason + cipher.id = null; + cipher.folderId = null; + cipher.organizationId = null; + cipher.collectionIds = null; + + // make sure password history is limited + if (cipher.passwordHistory != null && cipher.passwordHistory.length > 5) { + cipher.passwordHistory = cipher.passwordHistory.slice(0, 5); + } + + if (!this.organization && c.folderId != null && groupingsMap.has(c.folderId)) { + this.result.folderRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(c.folderId), + ]); + } else if (this.organization && c.collectionIds != null) { + c.collectionIds.forEach((cId) => { + if (groupingsMap.has(cId)) { + this.result.collectionRelationships.push([ + this.result.ciphers.length, + groupingsMap.get(cId), + ]); + } + }); + } + + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + + this.result.success = true; + } +} diff --git a/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts b/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts new file mode 100644 index 00000000..54da71b5 --- /dev/null +++ b/jslib/common/src/importers/bitwardenPasswordProtectedImporter.ts @@ -0,0 +1,81 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { KdfType } from "../enums/kdfType"; +import { EncString } from "../models/domain/encString"; +import { ImportResult } from "../models/domain/importResult"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +import { BitwardenJsonImporter } from "./bitwardenJsonImporter"; +import { Importer } from "./importer"; + +interface BitwardenPasswordProtectedFileFormat { + encrypted: boolean; + passwordProtected: boolean; + salt: string; + kdfIterations: number; + kdfType: number; + encKeyValidation_DO_NOT_EDIT: string; + data: string; +} + +export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter implements Importer { + private key: SymmetricCryptoKey; + + constructor(cryptoService: CryptoService, i18nService: I18nService, private password: string) { + super(cryptoService, i18nService); + } + + async parse(data: string): Promise { + const result = new ImportResult(); + const parsedData = JSON.parse(data); + if (this.cannotParseFile(parsedData)) { + result.success = false; + return result; + } + + if (!(await this.checkPassword(parsedData))) { + result.success = false; + result.errorMessage = this.i18nService.t("importEncKeyError"); + return result; + } + + const encData = new EncString(parsedData.data); + const clearTextData = await this.cryptoService.decryptToUtf8(encData, this.key); + return await super.parse(clearTextData); + } + + private async checkPassword(jdoc: BitwardenPasswordProtectedFileFormat): Promise { + this.key = await this.cryptoService.makePinKey( + this.password, + jdoc.salt, + KdfType.PBKDF2_SHA256, + jdoc.kdfIterations + ); + + const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT); + + const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8( + encKeyValidation, + this.key + ); + if (encKeyValidationDecrypt === null) { + return false; + } + return true; + } + + private cannotParseFile(jdoc: BitwardenPasswordProtectedFileFormat): boolean { + return ( + !jdoc || + !jdoc.encrypted || + !jdoc.passwordProtected || + !jdoc.salt || + !jdoc.kdfIterations || + typeof jdoc.kdfIterations !== "number" || + jdoc.kdfType == null || + KdfType[jdoc.kdfType] == null || + !jdoc.encKeyValidation_DO_NOT_EDIT || + !jdoc.data + ); + } +} diff --git a/jslib/common/src/importers/blackBerryCsvImporter.ts b/jslib/common/src/importers/blackBerryCsvImporter.ts new file mode 100644 index 00000000..ea3d08eb --- /dev/null +++ b/jslib/common/src/importers/blackBerryCsvImporter.ts @@ -0,0 +1,36 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BlackBerryCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.grouping === "list") { + return; + } + const cipher = this.initLoginCipher(); + cipher.favorite = value.fav === "1"; + cipher.name = this.getValueOrDefault(value.name); + cipher.notes = this.getValueOrDefault(value.extra); + if (value.grouping !== "note") { + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.username); + } + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/blurCsvImporter.ts b/jslib/common/src/importers/blurCsvImporter.ts new file mode 100644 index 00000000..ca9ecd1c --- /dev/null +++ b/jslib/common/src/importers/blurCsvImporter.ts @@ -0,0 +1,41 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class BlurCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.label === "null") { + value.label = null; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault( + value.label, + this.getValueOrDefault(this.nameFromUrl(value.domain), "--") + ); + cipher.login.uris = this.makeUriArray(value.domain); + cipher.login.password = this.getValueOrDefault(value.password); + + if (this.isNullOrWhitespace(value.email) && !this.isNullOrWhitespace(value.username)) { + cipher.login.username = value.username; + } else { + cipher.login.username = this.getValueOrDefault(value.email); + cipher.notes = this.getValueOrDefault(value.username); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/buttercupCsvImporter.ts b/jslib/common/src/importers/buttercupCsvImporter.ts new file mode 100644 index 00000000..aa3dda2d --- /dev/null +++ b/jslib/common/src/importers/buttercupCsvImporter.ts @@ -0,0 +1,50 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const OfficialProps = ["!group_id", "!group_name", "title", "username", "password", "URL", "id"]; + +export class ButtercupCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValueOrDefault(value["!group_name"])); + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.title, "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.URL); + + let processingCustomFields = false; + for (const prop in value) { + // eslint-disable-next-line + if (value.hasOwnProperty(prop)) { + if (!processingCustomFields && OfficialProps.indexOf(prop) === -1) { + processingCustomFields = true; + } + if (processingCustomFields) { + this.processKvp(cipher, prop, value[prop]); + } + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/chromeCsvImporter.ts b/jslib/common/src/importers/chromeCsvImporter.ts new file mode 100644 index 00000000..35c72ac3 --- /dev/null +++ b/jslib/common/src/importers/chromeCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ChromeCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/clipperzHtmlImporter.ts b/jslib/common/src/importers/clipperzHtmlImporter.ts new file mode 100644 index 00000000..c4f0402c --- /dev/null +++ b/jslib/common/src/importers/clipperzHtmlImporter.ts @@ -0,0 +1,88 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ClipperzHtmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const textarea = doc.querySelector("textarea"); + if (textarea == null || this.isNullOrWhitespace(textarea.textContent)) { + result.errorMessage = "Missing textarea."; + result.success = false; + return Promise.resolve(result); + } + + const entries = JSON.parse(textarea.textContent); + entries.forEach((entry: any) => { + const cipher = this.initLoginCipher(); + if (!this.isNullOrWhitespace(entry.label)) { + cipher.name = entry.label.split(" ")[0]; + } + if (entry.data != null && !this.isNullOrWhitespace(entry.data.notes)) { + cipher.notes = entry.data.notes.split("\\n").join("\n"); + } + + if (entry.currentVersion != null && entry.currentVersion.fields != null) { + for (const property in entry.currentVersion.fields) { + // eslint-disable-next-line + if (!entry.currentVersion.fields.hasOwnProperty(property)) { + continue; + } + + const field = entry.currentVersion.fields[property]; + const actionType = field.actionType != null ? field.actionType.toLowerCase() : null; + switch (actionType) { + case "password": + cipher.login.password = this.getValueOrDefault(field.value); + break; + case "email": + case "username": + case "user": + case "name": + cipher.login.username = this.getValueOrDefault(field.value); + break; + case "url": + cipher.login.uris = this.makeUriArray(field.value); + break; + default: { + const labelLower = field.label != null ? field.label.toLowerCase() : null; + if ( + cipher.login.password == null && + this.passwordFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.password = this.getValueOrDefault(field.value); + } else if ( + cipher.login.username == null && + this.usernameFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.username = this.getValueOrDefault(field.value); + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(labelLower) > -1 + ) { + cipher.login.uris = this.makeUriArray(field.value); + } else { + this.processKvp(cipher, field.label, field.value); + } + break; + } + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/codebookCsvImporter.ts b/jslib/common/src/importers/codebookCsvImporter.ts new file mode 100644 index 00000000..f842f21c --- /dev/null +++ b/jslib/common/src/importers/codebookCsvImporter.ts @@ -0,0 +1,47 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class CodebookCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValueOrDefault(value.Category)); + + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.Favorite) === "True"; + cipher.name = this.getValueOrDefault(value.Entry, "--"); + cipher.notes = this.getValueOrDefault(value.Note); + cipher.login.username = this.getValueOrDefault(value.Username, value.Email); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.totp = this.getValueOrDefault(value.TOTP); + cipher.login.uris = this.makeUriArray(value.Website); + + if (!this.isNullOrWhitespace(value.Username)) { + this.processKvp(cipher, "Email", value.Email); + } + this.processKvp(cipher, "Phone", value.Phone); + this.processKvp(cipher, "PIN", value.PIN); + this.processKvp(cipher, "Account", value.Account); + this.processKvp(cipher, "Date", value.Date); + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts b/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts new file mode 100644 index 00000000..2bb6ce14 --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/dashlaneCsvImporter.ts @@ -0,0 +1,271 @@ +import { CipherType } from "../../enums/cipherType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { + CredentialsRecord, + IdRecord, + PaymentsRecord, + PersonalInformationRecord, + SecureNoteRecord, +} from "./types/dashlaneCsvTypes"; + +const _mappedCredentialsColums = new Set([ + "title", + "note", + "username", + "password", + "url", + "otpSecret", + "category", +]); + +const _mappedPersonalInfoAsIdentiyColumns = new Set([ + "type", + "title", + "first_name", + "middle_name", + "last_name", + "login", + "email", + "phone_number", + "address", + "country", + "state", + "city", + "zip", + // Skip item_name as we already have set a combined name + "item_name", +]); + +const _mappedSecureNoteColumns = new Set(["title", "note"]); + +export class DashlaneCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + if (results[0].type != null && results[0].title != null) { + const personalRecords = results as PersonalInformationRecord[]; + + // If personalRecords has only one "name" then create an Identity-Cipher + if (personalRecords.filter((x) => x.type === "name").length === 1) { + const cipher = this.initLoginCipher(); + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + results.forEach((row) => { + this.parsePersonalInformationRecordAsIdentity(cipher, row); + }); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + result.success = true; + return Promise.resolve(result); + } + } + + results.forEach((row) => { + const cipher = this.initLoginCipher(); + + const rowKeys = Object.keys(row); + if (rowKeys[0] === "username") { + this.processFolder(result, row.category); + this.parseCredentialsRecord(cipher, row); + } + + if (rowKeys[0] === "type" && rowKeys[1] === "account_name") { + this.parsePaymentRecord(cipher, row); + } + + if (rowKeys[0] === "type" && rowKeys[1] === "number") { + this.parseIdRecord(cipher, row); + } + + if ((rowKeys[0] === "type") != null && rowKeys[1] === "title") { + this.parsePersonalInformationRecord(cipher, row); + } + + if (rowKeys[0] === "title" && rowKeys[1] === "note") { + this.parseSecureNoteRecords(cipher, row); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + parseCredentialsRecord(cipher: CipherView, row: CredentialsRecord) { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + + cipher.name = row.title; + cipher.notes = row.note; + cipher.login.username = row.username; + cipher.login.password = row.password; + cipher.login.totp = row.otpSecret; + cipher.login.uris = this.makeUriArray(row.url); + + this.importUnmappedFields(cipher, row, _mappedCredentialsColums); + } + + parsePaymentRecord(cipher: CipherView, row: PaymentsRecord) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + + cipher.name = row.account_name; + let mappedValues: string[] = []; + switch (row.type) { + case "credit_card": + cipher.card.cardholderName = row.account_name; + cipher.card.number = row.cc_number; + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = row.code; + cipher.card.expMonth = row.expiration_month; + cipher.card.expYear = row.expiration_year.substring(2, 4); + + // If you add more mapped fields please extend this + mappedValues = [ + "account_name", + "account_holder", + "cc_number", + "code", + "expiration_month", + "expiration_year", + ]; + break; + case "bank": + cipher.card.cardholderName = row.account_holder; + cipher.card.number = row.account_number; + + // If you add more mapped fields please extend this + mappedValues = ["account_name", "account_holder", "account_number"]; + break; + default: + break; + } + + this.importUnmappedFields(cipher, row, new Set(mappedValues)); + } + + parseIdRecord(cipher: CipherView, row: IdRecord) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + + const mappedValues: string[] = ["name", "number"]; + switch (row.type) { + case "card": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.licenseNumber = row.number; + break; + case "passport": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.passportNumber = row.number; + break; + case "license": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.licenseNumber = row.number; + cipher.identity.state = row.state; + + mappedValues.push("state"); + break; + case "social_security": + cipher.name = `${row.name} ${row.type}`; + this.processFullName(cipher, row.name); + cipher.identity.ssn = row.number; + break; + case "tax_number": + cipher.name = row.type; + cipher.identity.licenseNumber = row.number; + break; + + default: + break; + } + + // If you add more mapped fields please extend this + this.importUnmappedFields(cipher, row, new Set(mappedValues)); + } + + parsePersonalInformationRecord(cipher: CipherView, row: PersonalInformationRecord) { + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + if (row.type === "name") { + cipher.name = `${row.title} ${row.first_name} ${row.middle_name} ${row.last_name}` + .replace(" ", " ") + .trim(); + } else { + cipher.name = row.item_name; + } + + const dataRow = row as any; + Object.keys(row).forEach((key) => { + this.processKvp(cipher, key, dataRow[key]); + }); + } + + parsePersonalInformationRecordAsIdentity(cipher: CipherView, row: PersonalInformationRecord) { + switch (row.type) { + case "name": + this.processFullName(cipher, `${row.first_name} ${row.middle_name} ${row.last_name}`); + cipher.identity.title = row.title; + cipher.name = cipher.identity.fullName; + + cipher.identity.username = row.login; + break; + case "email": + cipher.identity.email = row.email; + break; + case "number": + cipher.identity.phone = row.phone_number; + break; + case "address": + cipher.identity.address1 = row.address; + cipher.identity.city = row.city; + cipher.identity.postalCode = row.zip; + cipher.identity.state = row.state; + cipher.identity.country = row.country; + break; + default: + break; + } + + this.importUnmappedFields(cipher, row, _mappedPersonalInfoAsIdentiyColumns); + } + + parseSecureNoteRecords(cipher: CipherView, row: SecureNoteRecord) { + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.name = row.title; + cipher.notes = row.note; + + this.importUnmappedFields(cipher, row, _mappedSecureNoteColumns); + } + + importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set) { + const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x)); + unmappedFields.forEach((key) => { + const item = row as any; + this.processKvp(cipher, key, item[key]); + }); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts b/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts new file mode 100644 index 00000000..cb7db59d --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/dashlaneJsonImporter.ts @@ -0,0 +1,171 @@ +import { CipherType } from "../../enums/cipherType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +const HandledResults = new Set([ + "ADDRESS", + "AUTHENTIFIANT", + "BANKSTATEMENT", + "IDCARD", + "IDENTITY", + "PAYMENTMEANS_CREDITCARD", + "PAYMENTMEAN_PAYPAL", + "EMAIL", +]); + +export class DashlaneJsonImporter extends BaseImporter implements Importer { + private result: ImportResult; + + parse(data: string): Promise { + this.result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.length === 0) { + this.result.success = false; + return Promise.resolve(this.result); + } + + if (results.ADDRESS != null) { + this.processAddress(results.ADDRESS); + } + if (results.AUTHENTIFIANT != null) { + this.processAuth(results.AUTHENTIFIANT); + } + if (results.BANKSTATEMENT != null) { + this.processNote(results.BANKSTATEMENT, "BankAccountName"); + } + if (results.IDCARD != null) { + this.processNote(results.IDCARD, "Fullname"); + } + if (results.PAYMENTMEANS_CREDITCARD != null) { + this.processCard(results.PAYMENTMEANS_CREDITCARD); + } + if (results.IDENTITY != null) { + this.processIdentity(results.IDENTITY); + } + + for (const key in results) { + // eslint-disable-next-line + if (results.hasOwnProperty(key) && !HandledResults.has(key)) { + this.processNote(results[key], null, "Generic Note"); + } + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processAuth(results: any[]) { + results.forEach((credential: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(credential.title); + + cipher.login.username = this.getValueOrDefault( + credential.login, + this.getValueOrDefault(credential.secondaryLogin) + ); + if (this.isNullOrWhitespace(cipher.login.username)) { + cipher.login.username = this.getValueOrDefault(credential.email); + } else if (!this.isNullOrWhitespace(credential.email)) { + cipher.notes = "Email: " + credential.email + "\n"; + } + + cipher.login.password = this.getValueOrDefault(credential.password); + cipher.login.uris = this.makeUriArray(credential.domain); + cipher.notes += this.getValueOrDefault(credential.note, ""); + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processIdentity(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.name = this.getValueOrDefault(obj.fullName, ""); + const nameParts = cipher.name.split(" "); + if (nameParts.length > 0) { + cipher.identity.firstName = this.getValueOrDefault(nameParts[0]); + } + if (nameParts.length === 2) { + cipher.identity.lastName = this.getValueOrDefault(nameParts[1]); + } else if (nameParts.length === 3) { + cipher.identity.middleName = this.getValueOrDefault(nameParts[1]); + cipher.identity.lastName = this.getValueOrDefault(nameParts[2]); + } + cipher.identity.username = this.getValueOrDefault(obj.pseudo); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processAddress(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.name = this.getValueOrDefault(obj.addressName); + cipher.identity.address1 = this.getValueOrDefault(obj.addressFull); + cipher.identity.city = this.getValueOrDefault(obj.city); + cipher.identity.state = this.getValueOrDefault(obj.state); + cipher.identity.postalCode = this.getValueOrDefault(obj.zipcode); + cipher.identity.country = this.getValueOrDefault(obj.country); + if (cipher.identity.country != null) { + cipher.identity.country = cipher.identity.country.toUpperCase(); + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processCard(results: any[]) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.card = new CardView(); + cipher.type = CipherType.Card; + cipher.name = this.getValueOrDefault(obj.bank); + cipher.card.number = this.getValueOrDefault(obj.cardNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.cardholderName = this.getValueOrDefault(obj.owner); + if (!this.isNullOrWhitespace(cipher.card.brand)) { + if (this.isNullOrWhitespace(cipher.name)) { + cipher.name = cipher.card.brand; + } else { + cipher.name += " - " + cipher.card.brand; + } + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } + + private processNote(results: any[], nameProperty: string, name: string = null) { + results.forEach((obj: any) => { + const cipher = new CipherView(); + cipher.secureNote = new SecureNoteView(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + if (name != null) { + cipher.name = name; + } else { + cipher.name = this.getValueOrDefault(obj[nameProperty]); + } + for (const key in obj) { + // eslint-disable-next-line + if (obj.hasOwnProperty(key) && key !== nameProperty) { + this.processKvp(cipher, key, obj[key].toString()); + } + } + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + } +} diff --git a/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts b/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts new file mode 100644 index 00000000..cb321c56 --- /dev/null +++ b/jslib/common/src/importers/dashlaneImporters/types/dashlaneCsvTypes.ts @@ -0,0 +1,68 @@ +// tslint:disable +export class CredentialsRecord { + username: string; + username2: string; + username3: string; + title: string; + password: string; + note: string; + url: string; + category: string; + otpSecret: string; +} + +export class PaymentsRecord { + type: string; + account_name: string; + account_holder: string; + cc_number: string; + code: string; + expiration_month: string; + expiration_year: string; + routing_number: string; + account_number: string; + country: string; + issuing_bank: string; +} + +export class IdRecord { + type: string; + number: string; + name: string; + issue_date: string; + expiration_date: string; + place_of_issue: string; + state: string; +} + +export class PersonalInformationRecord { + type: string; + title: string; + first_name: string; + middle_name: string; + last_name: string; + login: string; + date_of_birth: string; + place_of_birth: string; + email: string; + email_type: string; + item_name: string; + phone_number: string; + address: string; + country: string; + state: string; + city: string; + zip: string; + address_recipient: string; + address_building: string; + address_apartment: string; + address_floor: string; + address_door_code: string; + job_title: string; + url: string; +} + +export class SecureNoteRecord { + title: string; + note: string; +} diff --git a/jslib/common/src/importers/encryptrCsvImporter.ts b/jslib/common/src/importers/encryptrCsvImporter.ts new file mode 100644 index 00000000..8c3e9e08 --- /dev/null +++ b/jslib/common/src/importers/encryptrCsvImporter.ts @@ -0,0 +1,60 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EncryptrCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Label, "--"); + cipher.notes = this.getValueOrDefault(value.Notes); + const text = this.getValueOrDefault(value.Text); + if (!this.isNullOrWhitespace(text)) { + if (this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = text; + } else { + cipher.notes += "\n\n" + text; + } + } + + const type = value["Entry Type"]; + if (type === "Password") { + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value["Site URL"]); + } else if (type === "Credit Card") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value["Name on card"]); + cipher.card.number = this.getValueOrDefault(value["Card Number"]); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.CVV); + const expiry = this.getValueOrDefault(value.Expiry); + if (!this.isNullOrWhitespace(expiry)) { + const expParts = expiry.split("/"); + if (expParts.length > 1) { + cipher.card.expMonth = parseInt(expParts[0], null).toString(); + cipher.card.expYear = (2000 + parseInt(expParts[1], null)).toString(); + } + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/enpassCsvImporter.ts b/jslib/common/src/importers/enpassCsvImporter.ts new file mode 100644 index 00000000..b5eb0b60 --- /dev/null +++ b/jslib/common/src/importers/enpassCsvImporter.ts @@ -0,0 +1,133 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EnpassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let firstRow = true; + results.forEach((value) => { + if (value.length < 2 || (firstRow && (value[0] === "Title" || value[0] === "title"))) { + firstRow = false; + return; + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 1]); + cipher.name = this.getValueOrDefault(value[0], "--"); + + if ( + value.length === 2 || + (!this.containsField(value, "username") && + !this.containsField(value, "password") && + !this.containsField(value, "email") && + !this.containsField(value, "url")) + ) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } + + if ( + this.containsField(value, "cardholder") && + this.containsField(value, "number") && + this.containsField(value, "expiry date") + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } + + if (value.length > 2 && value.length % 2 === 0) { + for (let i = 0; i < value.length - 2; i += 2) { + const fieldValue: string = value[i + 2]; + if (this.isNullOrWhitespace(fieldValue)) { + continue; + } + + const fieldName: string = value[i + 1]; + const fieldNameLower = fieldName.toLowerCase(); + + if (cipher.type === CipherType.Login) { + if ( + fieldNameLower === "url" && + (cipher.login.uris == null || cipher.login.uris.length === 0) + ) { + cipher.login.uris = this.makeUriArray(fieldValue); + continue; + } else if ( + (fieldNameLower === "username" || fieldNameLower === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = fieldValue; + continue; + } else if ( + fieldNameLower === "password" && + this.isNullOrWhitespace(cipher.login.password) + ) { + cipher.login.password = fieldValue; + continue; + } else if (fieldNameLower === "totp" && this.isNullOrWhitespace(cipher.login.totp)) { + cipher.login.totp = fieldValue; + continue; + } + } else if (cipher.type === CipherType.Card) { + if ( + fieldNameLower === "cardholder" && + this.isNullOrWhitespace(cipher.card.cardholderName) + ) { + cipher.card.cardholderName = fieldValue; + continue; + } else if (fieldNameLower === "number" && this.isNullOrWhitespace(cipher.card.number)) { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + continue; + } else if (fieldNameLower === "cvc" && this.isNullOrWhitespace(cipher.card.code)) { + cipher.card.code = fieldValue; + continue; + } else if ( + fieldNameLower === "expiry date" && + this.isNullOrWhitespace(cipher.card.expMonth) && + this.isNullOrWhitespace(cipher.card.expYear) + ) { + if (this.setCardExpiration(cipher, fieldValue)) { + continue; + } + } else if (fieldNameLower === "type") { + // Skip since brand was determined from number above + continue; + } + } + + this.processKvp(cipher, fieldName, fieldValue); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private containsField(fields: any[], name: string) { + if (fields == null || name == null) { + return false; + } + return ( + fields.filter((f) => !this.isNullOrWhitespace(f) && f.toLowerCase() === name.toLowerCase()) + .length > 0 + ); + } +} diff --git a/jslib/common/src/importers/enpassJsonImporter.ts b/jslib/common/src/importers/enpassJsonImporter.ts new file mode 100644 index 00000000..033c5995 --- /dev/null +++ b/jslib/common/src/importers/enpassJsonImporter.ts @@ -0,0 +1,191 @@ +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class EnpassJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.items == null || results.items.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + const foldersIndexMap = new Map(); + const folderTree = this.buildFolderTree(results.folders); + this.flattenFolderTree(null, folderTree, foldersMap); + foldersMap.forEach((val, key) => { + foldersIndexMap.set(key, result.folders.length); + const f = new FolderView(); + f.name = val; + result.folders.push(f); + }); + + results.items.forEach((item: any) => { + if (item.folders != null && item.folders.length > 0 && foldersIndexMap.has(item.folders[0])) { + result.folderRelationships.push([ + result.ciphers.length, + foldersIndexMap.get(item.folders[0]), + ]); + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(item.title); + cipher.favorite = item.favorite > 0; + + if (item.template_type != null && item.fields != null && item.fields.length > 0) { + if ( + item.template_type.indexOf("login.") === 0 || + item.template_type.indexOf("password.") === 0 + ) { + this.processLogin(cipher, item.fields); + } else if (item.template_type.indexOf("creditcard.") === 0) { + this.processCard(cipher, item.fields); + } else if ( + item.template_type.indexOf("identity.") < 0 && + item.fields.some((f: any) => f.type === "password" && !this.isNullOrWhitespace(f.value)) + ) { + this.processLogin(cipher, item.fields); + } else { + this.processNote(cipher, item.fields); + } + } + + cipher.notes += "\n" + this.getValueOrDefault(item.note, ""); + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private processLogin(cipher: CipherView, fields: any[]) { + const urls: string[] = []; + fields.forEach((field: any) => { + if (this.isNullOrWhitespace(field.value) || field.type === "section") { + return; + } + + if ( + (field.type === "username" || field.type === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = field.value; + } else if (field.type === "password" && this.isNullOrWhitespace(cipher.login.password)) { + cipher.login.password = field.value; + } else if (field.type === "totp" && this.isNullOrWhitespace(cipher.login.totp)) { + cipher.login.totp = field.value; + } else if (field.type === "url") { + urls.push(field.value); + } else { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + + private processCard(cipher: CipherView, fields: any[]) { + cipher.card = new CardView(); + cipher.type = CipherType.Card; + fields.forEach((field: any) => { + if ( + this.isNullOrWhitespace(field.value) || + field.type === "section" || + field.type === "ccType" + ) { + return; + } + + if (field.type === "ccName" && this.isNullOrWhitespace(cipher.card.cardholderName)) { + cipher.card.cardholderName = field.value; + } else if (field.type === "ccNumber" && this.isNullOrWhitespace(cipher.card.number)) { + cipher.card.number = field.value; + cipher.card.brand = this.getCardBrand(cipher.card.number); + } else if (field.type === "ccCvc" && this.isNullOrWhitespace(cipher.card.code)) { + cipher.card.code = field.value; + } else if (field.type === "ccExpiry" && this.isNullOrWhitespace(cipher.card.expYear)) { + if (!this.setCardExpiration(cipher, field.value)) { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + } else { + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + } + }); + } + + private processNote(cipher: CipherView, fields: any[]) { + fields.forEach((field: any) => { + if (this.isNullOrWhitespace(field.value) || field.type === "section") { + return; + } + this.processKvp( + cipher, + field.label, + field.value, + field.sensitive === 1 ? FieldType.Hidden : FieldType.Text + ); + }); + } + + private buildFolderTree(folders: any[]): any[] { + if (folders == null) { + return []; + } + const folderTree: any[] = []; + const map = new Map([]); + folders.forEach((obj: any) => { + map.set(obj.uuid, obj); + obj.children = []; + }); + folders.forEach((obj: any) => { + if (obj.parent_uuid != null && obj.parent_uuid !== "" && map.has(obj.parent_uuid)) { + map.get(obj.parent_uuid).children.push(obj); + } else { + folderTree.push(obj); + } + }); + return folderTree; + } + + private flattenFolderTree(titlePrefix: string, tree: any[], map: Map) { + if (tree == null) { + return; + } + tree.forEach((f: any) => { + if (f.title != null && f.title.trim() !== "") { + let title = f.title.trim(); + if (titlePrefix != null && titlePrefix.trim() !== "") { + title = titlePrefix + "/" + title; + } + map.set(f.uuid, title); + if (f.children != null && f.children.length !== 0) { + this.flattenFolderTree(title, f.children, map); + } + } + }); + } +} diff --git a/jslib/common/src/importers/firefoxCsvImporter.ts b/jslib/common/src/importers/firefoxCsvImporter.ts new file mode 100644 index 00000000..1b71aac9 --- /dev/null +++ b/jslib/common/src/importers/firefoxCsvImporter.ts @@ -0,0 +1,33 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class FirefoxCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results + .filter((value) => { + return value.url !== "chrome://FirefoxAccounts"; + }) + .forEach((value) => { + const cipher = this.initLoginCipher(); + const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname)); + cipher.name = this.getValueOrDefault(this.nameFromUrl(url), "--"); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/fsecureFskImporter.ts b/jslib/common/src/importers/fsecureFskImporter.ts new file mode 100644 index 00000000..c81a0820 --- /dev/null +++ b/jslib/common/src/importers/fsecureFskImporter.ts @@ -0,0 +1,59 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class FSecureFskImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.data == null) { + result.success = false; + return Promise.resolve(result); + } + + for (const key in results.data) { + // eslint-disable-next-line + if (!results.data.hasOwnProperty(key)) { + continue; + } + + const value = results.data[key]; + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.service); + cipher.notes = this.getValueOrDefault(value.notes); + + if (value.style === "website" || value.style === "globe") { + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + } else if (value.style === "creditcard") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.username); + cipher.card.number = this.getValueOrDefault(value.creditNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.creditCvv); + if (!this.isNullOrWhitespace(value.creditExpiry)) { + if (!this.setCardExpiration(cipher, value.creditExpiry)) { + this.processKvp(cipher, "Expiration", value.creditExpiry); + } + } + if (!this.isNullOrWhitespace(value.password)) { + this.processKvp(cipher, "PIN", value.password); + } + } else { + continue; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/gnomeJsonImporter.ts b/jslib/common/src/importers/gnomeJsonImporter.ts new file mode 100644 index 00000000..1dfac675 --- /dev/null +++ b/jslib/common/src/importers/gnomeJsonImporter.ts @@ -0,0 +1,71 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class GnomeJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || Object.keys(results).length === 0) { + result.success = false; + return Promise.resolve(result); + } + + for (const keyRing in results) { + if ( + !results.hasOwnProperty(keyRing) || // eslint-disable-line + this.isNullOrWhitespace(keyRing) || + results[keyRing].length === 0 + ) { + continue; + } + + results[keyRing].forEach((value: any) => { + if ( + this.isNullOrWhitespace(value.display_name) || + value.display_name.indexOf("http") !== 0 + ) { + return; + } + + this.processFolder(result, keyRing); + const cipher = this.initLoginCipher(); + cipher.name = value.display_name.replace("http://", "").replace("https://", ""); + if (cipher.name.length > 30) { + cipher.name = cipher.name.substring(0, 30); + } + cipher.login.password = this.getValueOrDefault(value.secret); + cipher.login.uris = this.makeUriArray(value.display_name); + + if (value.attributes != null) { + cipher.login.username = + value.attributes != null + ? this.getValueOrDefault(value.attributes.username_value) + : null; + for (const attr in value.attributes) { + if ( + !value.attributes.hasOwnProperty(attr) || // eslint-disable-line + attr === "username_value" || + attr === "xdg:schema" + ) { + continue; + } + this.processKvp(cipher, attr, value.attributes[attr]); + } + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + } + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/importError.ts b/jslib/common/src/importers/importError.ts new file mode 100644 index 00000000..6a74d66e --- /dev/null +++ b/jslib/common/src/importers/importError.ts @@ -0,0 +1,5 @@ +export class ImportError extends Error { + constructor(message?: string, public passwordRequired: boolean = false) { + super(message); + } +} diff --git a/jslib/common/src/importers/importer.ts b/jslib/common/src/importers/importer.ts new file mode 100644 index 00000000..a62cc6c1 --- /dev/null +++ b/jslib/common/src/importers/importer.ts @@ -0,0 +1,6 @@ +import { ImportResult } from "../models/domain/importResult"; + +export interface Importer { + organizationId: string; + parse(data: string): Promise; +} diff --git a/jslib/common/src/importers/kasperskyTxtImporter.ts b/jslib/common/src/importers/kasperskyTxtImporter.ts new file mode 100644 index 00000000..b049f2bb --- /dev/null +++ b/jslib/common/src/importers/kasperskyTxtImporter.ts @@ -0,0 +1,124 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const NotesHeader = "Notes\n\n"; +const ApplicationsHeader = "Applications\n\n"; +const WebsitesHeader = "Websites\n\n"; +const Delimiter = "\n---\n"; + +export class KasperskyTxtImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + + let notesData: string; + let applicationsData: string; + let websitesData: string; + let workingData = this.splitNewLine(data).join("\n"); + + if (workingData.indexOf(NotesHeader) !== -1) { + const parts = workingData.split(NotesHeader); + if (parts.length > 1) { + workingData = parts[0]; + notesData = parts[1]; + } + } + if (workingData.indexOf(ApplicationsHeader) !== -1) { + const parts = workingData.split(ApplicationsHeader); + if (parts.length > 1) { + workingData = parts[0]; + applicationsData = parts[1]; + } + } + if (workingData.indexOf(WebsitesHeader) === 0) { + const parts = workingData.split(WebsitesHeader); + if (parts.length > 1) { + workingData = parts[0]; + websitesData = parts[1]; + } + } + + const notes = this.parseDataCategory(notesData); + const applications = this.parseDataCategory(applicationsData); + const websites = this.parseDataCategory(websitesData); + + notes.forEach((n) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(n.get("Name")); + cipher.notes = this.getValueOrDefault(n.get("Text")); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + websites.concat(applications).forEach((w) => { + const cipher = this.initLoginCipher(); + const nameKey = w.has("Website name") ? "Website name" : "Application"; + cipher.name = this.getValueOrDefault(w.get(nameKey), ""); + if (!this.isNullOrWhitespace(w.get("Login name"))) { + if (!this.isNullOrWhitespace(cipher.name)) { + cipher.name += ": "; + } + cipher.name += w.get("Login name"); + } + cipher.notes = this.getValueOrDefault(w.get("Comment")); + if (w.has("Website URL")) { + cipher.login.uris = this.makeUriArray(w.get("Website URL")); + } + cipher.login.username = this.getValueOrDefault(w.get("Login")); + cipher.login.password = this.getValueOrDefault(w.get("Password")); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + private parseDataCategory(data: string): Map[] { + if (this.isNullOrWhitespace(data) || data.indexOf(Delimiter) === -1) { + return []; + } + const items: Map[] = []; + data.split(Delimiter).forEach((p) => { + if (p.indexOf("\n") === -1) { + return; + } + const item = new Map(); + let itemComment: string; + let itemCommentKey: string; + p.split("\n").forEach((l) => { + if (itemComment != null) { + itemComment += "\n" + l; + return; + } + const colonIndex = l.indexOf(":"); + let key: string; + let val: string; + if (colonIndex === -1) { + return; + } else { + key = l.substring(0, colonIndex); + if (l.length > colonIndex + 1) { + val = l.substring(colonIndex + 2); + } + } + if (key != null) { + item.set(key, val); + } + if (key === "Comment" || key === "Text") { + itemComment = val; + itemCommentKey = key; + } + }); + if (itemComment != null && itemCommentKey != null) { + item.set(itemCommentKey, itemComment); + } + if (item.size === 0) { + return; + } + items.push(item); + }); + return items; + } +} diff --git a/jslib/common/src/importers/keepass2XmlImporter.ts b/jslib/common/src/importers/keepass2XmlImporter.ts new file mode 100644 index 00000000..2ed5d0f9 --- /dev/null +++ b/jslib/common/src/importers/keepass2XmlImporter.ts @@ -0,0 +1,101 @@ +import { FieldType } from "../enums/fieldType"; +import { ImportResult } from "../models/domain/importResult"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class KeePass2XmlImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + const doc = this.parseXml(data); + if (doc == null) { + this.result.success = false; + return Promise.resolve(this.result); + } + + const rootGroup = doc.querySelector("KeePassFile > Root > Group"); + if (rootGroup == null) { + this.result.errorMessage = "Missing `KeePassFile > Root > Group` node."; + this.result.success = false; + return Promise.resolve(this.result); + } + + this.traverse(rootGroup, true, ""); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + traverse(node: Element, isRootNode: boolean, groupPrefixName: string) { + const folderIndex = this.result.folders.length; + let groupName = groupPrefixName; + + if (!isRootNode) { + if (groupName !== "") { + groupName += "/"; + } + const nameEl = this.querySelectorDirectChild(node, "Name"); + groupName += nameEl == null ? "-" : nameEl.textContent; + const folder = new FolderView(); + folder.name = groupName; + this.result.folders.push(folder); + } + + this.querySelectorAllDirectChild(node, "Entry").forEach((entry) => { + const cipherIndex = this.result.ciphers.length; + + const cipher = this.initLoginCipher(); + this.querySelectorAllDirectChild(entry, "String").forEach((entryString) => { + const valueEl = this.querySelectorDirectChild(entryString, "Value"); + const value = valueEl != null ? valueEl.textContent : null; + if (this.isNullOrWhitespace(value)) { + return; + } + const keyEl = this.querySelectorDirectChild(entryString, "Key"); + const key = keyEl != null ? keyEl.textContent : null; + + if (key === "URL") { + cipher.login.uris = this.makeUriArray(value); + } else if (key === "UserName") { + cipher.login.username = value; + } else if (key === "Password") { + cipher.login.password = value; + } else if (key === "otp") { + cipher.login.totp = value.replace("key=", ""); + } else if (key === "Title") { + cipher.name = value; + } else if (key === "Notes") { + cipher.notes += value + "\n"; + } else { + let type = FieldType.Text; + const attrs = valueEl.attributes as any; + if ( + attrs.length > 0 && + attrs.ProtectInMemory != null && + attrs.ProtectInMemory.value === "True" + ) { + type = FieldType.Hidden; + } + this.processKvp(cipher, key, value, type); + } + }); + + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + + if (!isRootNode) { + this.result.folderRelationships.push([cipherIndex, folderIndex]); + } + }); + + this.querySelectorAllDirectChild(node, "Group").forEach((group) => { + this.traverse(group, false, groupName); + }); + } +} diff --git a/jslib/common/src/importers/keepassxCsvImporter.ts b/jslib/common/src/importers/keepassxCsvImporter.ts new file mode 100644 index 00000000..58640d5a --- /dev/null +++ b/jslib/common/src/importers/keepassxCsvImporter.ts @@ -0,0 +1,44 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class KeePassXCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.isNullOrWhitespace(value.Title)) { + return; + } + + value.Group = + !this.isNullOrWhitespace(value.Group) && value.Group.startsWith("Root/") + ? value.Group.replace("Root/", "") + : value.Group; + const groupName = !this.isNullOrWhitespace(value.Group) ? value.Group : null; + this.processFolder(result, groupName); + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.name = this.getValueOrDefault(value.Title, "--"); + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts b/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts new file mode 100644 index 00000000..e9c65d71 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/keeperCsvImporter.ts @@ -0,0 +1,45 @@ +import { ImportResult } from "../../models/domain/importResult"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +export class KeeperCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 6) { + return; + } + + this.processFolder(result, value[0]); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[5]) + "\n"; + cipher.name = this.getValueOrDefault(value[1], "--"); + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[4]); + + if (value.length > 7) { + // we have some custom fields. + for (let i = 7; i < value.length; i = i + 2) { + this.processKvp(cipher, value[i], value[i + 1]); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts b/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts new file mode 100644 index 00000000..7ee80b08 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/keeperJsonImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../../models/domain/importResult"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { KeeperJsonExport, RecordsEntity } from "./types/keeperJsonTypes"; + +export class KeeperJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const keeperExport: KeeperJsonExport = JSON.parse(data); + if (keeperExport == null || keeperExport.records == null || keeperExport.records.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + keeperExport.records.forEach((record) => { + this.parseFolders(result, record); + + const cipher = this.initLoginCipher(); + cipher.name = record.title; + cipher.login.username = record.login; + cipher.login.password = record.password; + + cipher.login.uris = this.makeUriArray(record.login_url); + cipher.notes = record.notes; + + if (record.custom_fields != null) { + let customfieldKeys = Object.keys(record.custom_fields); + if (record.custom_fields["TFC:Keeper"] != null) { + customfieldKeys = customfieldKeys.filter((item) => item !== "TFC:Keeper"); + cipher.login.totp = record.custom_fields["TFC:Keeper"]; + } + + customfieldKeys.forEach((key) => { + this.processKvp(cipher, key, record.custom_fields[key]); + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseFolders(result: ImportResult, record: RecordsEntity) { + if (record.folders == null || record.folders.length === 0) { + return; + } + + record.folders.forEach((item) => { + if (item.folder != null) { + this.processFolder(result, item.folder); + return; + } + + if (item.shared_folder != null) { + this.processFolder(result, item.shared_folder); + return; + } + }); + } +} diff --git a/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts b/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts new file mode 100644 index 00000000..1f6d2ea4 --- /dev/null +++ b/jslib/common/src/importers/keeperImporters/types/keeperJsonTypes.ts @@ -0,0 +1,41 @@ +export interface KeeperJsonExport { + shared_folders?: SharedFoldersEntity[] | null; + records?: RecordsEntity[] | null; +} + +export interface SharedFoldersEntity { + path: string; + manage_users: boolean; + manage_records: boolean; + can_edit: boolean; + can_share: boolean; + permissions?: PermissionsEntity[] | null; +} + +export interface PermissionsEntity { + uid?: string | null; + manage_users: boolean; + manage_records: boolean; + name?: string | null; +} + +export interface RecordsEntity { + title: string; + login: string; + password: string; + login_url: string; + notes?: string; + custom_fields?: CustomFields; + folders?: FoldersEntity[] | null; +} + +export type CustomFields = { + [key: string]: string | null; +}; + +export interface FoldersEntity { + folder?: string | null; + shared_folder?: string | null; + can_edit?: boolean | null; + can_share?: boolean | null; +} diff --git a/jslib/common/src/importers/lastpassCsvImporter.ts b/jslib/common/src/importers/lastpassCsvImporter.ts new file mode 100644 index 00000000..5c016028 --- /dev/null +++ b/jslib/common/src/importers/lastpassCsvImporter.ts @@ -0,0 +1,285 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { FolderView } from "../models/view/folderView"; +import { IdentityView } from "../models/view/identityView"; +import { LoginView } from "../models/view/loginView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class LastPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipherIndex = result.ciphers.length; + let folderIndex = result.folders.length; + let grouping = value.grouping; + if (grouping != null) { + // eslint-disable-next-line + grouping = grouping.replace(/\\/g, "/").replace(/[\x00-\x1F\x7F-\x9F]/g, ""); + } + const hasFolder = this.getValueOrDefault(grouping, "(none)") !== "(none)"; + let addFolder = hasFolder; + + if (hasFolder) { + for (let i = 0; i < result.folders.length; i++) { + if (result.folders[i].name === grouping) { + addFolder = false; + folderIndex = i; + break; + } + } + } + + const cipher = this.buildBaseCipher(value); + if (cipher.type === CipherType.Login) { + cipher.notes = this.getValueOrDefault(value.extra); + cipher.login = new LoginView(); + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.totp = this.getValueOrDefault(value.totp); + } else if (cipher.type === CipherType.SecureNote) { + this.parseSecureNote(value, cipher); + } else if (cipher.type === CipherType.Card) { + cipher.card = this.parseCard(value); + cipher.notes = this.getValueOrDefault(value.notes); + } else if (cipher.type === CipherType.Identity) { + cipher.identity = this.parseIdentity(value); + cipher.notes = this.getValueOrDefault(value.notes); + if (!this.isNullOrWhitespace(value.ccnum)) { + // there is a card on this identity too + const cardCipher = this.buildBaseCipher(value); + cardCipher.identity = null; + cardCipher.type = CipherType.Card; + cardCipher.card = this.parseCard(value); + result.ciphers.push(cardCipher); + } + } + + result.ciphers.push(cipher); + + if (addFolder) { + const f = new FolderView(); + f.name = grouping; + result.folders.push(f); + } + if (hasFolder) { + result.folderRelationships.push([cipherIndex, folderIndex]); + } + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private buildBaseCipher(value: any) { + const cipher = new CipherView(); + // eslint-disable-next-line + if (value.hasOwnProperty("profilename") && value.hasOwnProperty("profilelanguage")) { + // form fill + cipher.favorite = false; + cipher.name = this.getValueOrDefault(value.profilename, "--"); + cipher.type = CipherType.Card; + + if ( + !this.isNullOrWhitespace(value.title) || + !this.isNullOrWhitespace(value.firstname) || + !this.isNullOrWhitespace(value.lastname) || + !this.isNullOrWhitespace(value.address1) || + !this.isNullOrWhitespace(value.phone) || + !this.isNullOrWhitespace(value.username) || + !this.isNullOrWhitespace(value.email) + ) { + cipher.type = CipherType.Identity; + } + } else { + // site or secure note + cipher.favorite = !this.organization && this.getValueOrDefault(value.fav, "0") === "1"; + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.type = value.url === "http://sn" ? CipherType.SecureNote : CipherType.Login; + } + return cipher; + } + + private parseCard(value: any): CardView { + const card = new CardView(); + card.cardholderName = this.getValueOrDefault(value.ccname); + card.number = this.getValueOrDefault(value.ccnum); + card.code = this.getValueOrDefault(value.cccsc); + card.brand = this.getCardBrand(value.ccnum); + + if (!this.isNullOrWhitespace(value.ccexp) && value.ccexp.indexOf("-") > -1) { + const ccexpParts = (value.ccexp as string).split("-"); + if (ccexpParts.length > 1) { + card.expYear = ccexpParts[0]; + card.expMonth = ccexpParts[1]; + if (card.expMonth.length === 2 && card.expMonth[0] === "0") { + card.expMonth = card.expMonth[1]; + } + } + } + + return card; + } + + private parseIdentity(value: any): IdentityView { + const identity = new IdentityView(); + identity.title = this.getValueOrDefault(value.title); + identity.firstName = this.getValueOrDefault(value.firstname); + identity.middleName = this.getValueOrDefault(value.middlename); + identity.lastName = this.getValueOrDefault(value.lastname); + identity.username = this.getValueOrDefault(value.username); + identity.company = this.getValueOrDefault(value.company); + identity.ssn = this.getValueOrDefault(value.ssn); + identity.address1 = this.getValueOrDefault(value.address1); + identity.address2 = this.getValueOrDefault(value.address2); + identity.address3 = this.getValueOrDefault(value.address3); + identity.city = this.getValueOrDefault(value.city); + identity.state = this.getValueOrDefault(value.state); + identity.postalCode = this.getValueOrDefault(value.zip); + identity.country = this.getValueOrDefault(value.country); + identity.email = this.getValueOrDefault(value.email); + identity.phone = this.getValueOrDefault(value.phone); + + if (!this.isNullOrWhitespace(identity.title)) { + identity.title = identity.title.charAt(0).toUpperCase() + identity.title.slice(1); + } + + return identity; + } + + private parseSecureNote(value: any, cipher: CipherView) { + const extraParts = this.splitNewLine(value.extra); + let processedNote = false; + + if (extraParts.length) { + const typeParts = extraParts[0].split(":"); + if ( + typeParts.length > 1 && + typeParts[0] === "NoteType" && + (typeParts[1] === "Credit Card" || typeParts[1] === "Address") + ) { + if (typeParts[1] === "Credit Card") { + const mappedData = this.parseSecureNoteMapping(cipher, extraParts, { + Number: "number", + "Name on Card": "cardholderName", + "Security Code": "code", + // LP provides date in a format like 'June,2020' + // Store in expMonth, then parse and modify + "Expiration Date": "expMonth", + }); + + if (this.isNullOrWhitespace(mappedData.expMonth) || mappedData.expMonth === ",") { + // No expiration data + mappedData.expMonth = undefined; + } else { + const [monthString, year] = mappedData.expMonth.split(","); + // Parse month name into number + if (!this.isNullOrWhitespace(monthString)) { + const month = new Date(Date.parse(monthString.trim() + " 1, 2012")).getMonth() + 1; + if (isNaN(month)) { + mappedData.expMonth = undefined; + } else { + mappedData.expMonth = month.toString(); + } + } else { + mappedData.expMonth = undefined; + } + if (!this.isNullOrWhitespace(year)) { + mappedData.expYear = year; + } + } + + cipher.type = CipherType.Card; + cipher.card = mappedData; + } else if (typeParts[1] === "Address") { + const mappedData = this.parseSecureNoteMapping(cipher, extraParts, { + Title: "title", + "First Name": "firstName", + "Last Name": "lastName", + "Middle Name": "middleName", + Company: "company", + "Address 1": "address1", + "Address 2": "address2", + "Address 3": "address3", + "City / Town": "city", + State: "state", + "Zip / Postal Code": "postalCode", + Country: "country", + "Email Address": "email", + Username: "username", + }); + cipher.type = CipherType.Identity; + cipher.identity = mappedData; + } + processedNote = true; + } + } + + if (!processedNote) { + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + cipher.notes = this.getValueOrDefault(value.extra); + } + } + + private parseSecureNoteMapping(cipher: CipherView, extraParts: string[], map: any): T { + const dataObj: any = {}; + + let processingNotes = false; + extraParts.forEach((extraPart) => { + let key: string = null; + let val: string = null; + if (!processingNotes) { + if (this.isNullOrWhitespace(extraPart)) { + return; + } + const colonIndex = extraPart.indexOf(":"); + if (colonIndex === -1) { + key = extraPart; + } else { + key = extraPart.substring(0, colonIndex); + if (extraPart.length > colonIndex) { + val = extraPart.substring(colonIndex + 1); + } + } + if (this.isNullOrWhitespace(key) || this.isNullOrWhitespace(val) || key === "NoteType") { + return; + } + } + + if (processingNotes) { + cipher.notes += "\n" + extraPart; + } else if (key === "Notes") { + if (!this.isNullOrWhitespace(cipher.notes)) { + cipher.notes += "\n" + val; + } else { + cipher.notes = val; + } + processingNotes = true; + // eslint-disable-next-line + } else if (map.hasOwnProperty(key)) { + dataObj[map[key]] = val; + } else { + this.processKvp(cipher, key, val); + } + }); + + return dataObj; + } +} diff --git a/jslib/common/src/importers/logMeOnceCsvImporter.ts b/jslib/common/src/importers/logMeOnceCsvImporter.ts new file mode 100644 index 00000000..698ebc80 --- /dev/null +++ b/jslib/common/src/importers/logMeOnceCsvImporter.ts @@ -0,0 +1,31 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class LogMeOnceCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 4) { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[1]); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/meldiumCsvImporter.ts b/jslib/common/src/importers/meldiumCsvImporter.ts new file mode 100644 index 00000000..79835a90 --- /dev/null +++ b/jslib/common/src/importers/meldiumCsvImporter.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class MeldiumCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.DisplayName, "--"); + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.login.username = this.getValueOrDefault(value.UserName); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.Url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/msecureCsvImporter.ts b/jslib/common/src/importers/msecureCsvImporter.ts new file mode 100644 index 00000000..583d13d7 --- /dev/null +++ b/jslib/common/src/importers/msecureCsvImporter.ts @@ -0,0 +1,61 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class MSecureCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 3) { + return; + } + + const folderName = + this.getValueOrDefault(value[0], "Unassigned") !== "Unassigned" ? value[0] : null; + this.processFolder(result, folderName); + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[2], "--"); + + if (value[1] === "Web Logins" || value[1] === "Login") { + cipher.login.uris = this.makeUriArray(value[4]); + cipher.login.username = this.getValueOrDefault(value[5]); + cipher.login.password = this.getValueOrDefault(value[6]); + cipher.notes = !this.isNullOrWhitespace(value[3]) ? value[3].split("\\n").join("\n") : null; + } else if (value.length > 3) { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + for (let i = 3; i < value.length; i++) { + if (!this.isNullOrWhitespace(value[i])) { + cipher.notes += value[i] + "\n"; + } + } + } + + if (!this.isNullOrWhitespace(value[1]) && cipher.type !== CipherType.Login) { + cipher.name = value[1] + ": " + cipher.name; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/mykiCsvImporter.ts b/jslib/common/src/importers/mykiCsvImporter.ts new file mode 100644 index 00000000..d9a05e1e --- /dev/null +++ b/jslib/common/src/importers/mykiCsvImporter.ts @@ -0,0 +1,157 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { CipherView } from "../models/view/cipherView"; +import { IdentityView } from "../models/view/identityView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const mappedBaseColumns = ["nickname", "additionalInfo"]; +const _mappedUserAccountColumns = new Set( + mappedBaseColumns.concat(["url", "username", "password", "twofaSecret"]) +); +const _mappedCreditCardColumns = new Set( + mappedBaseColumns.concat(["cardNumber", "cardName", "exp_month", "exp_year", "cvv"]) +); + +const _mappedIdentityColumns = new Set( + mappedBaseColumns.concat([ + "title", + "firstName", + "middleName", + "lastName", + "email", + "firstAddressLine", + "secondAddressLine", + "city", + "country", + "zipCode", + ]) +); + +const _mappedIdCardColumns = new Set(mappedBaseColumns.concat(["idName", "idNumber", "idCountry"])); + +const _mappedTwoFaColumns = new Set(mappedBaseColumns.concat(["authToken"])); + +const _mappedUserNoteColumns = new Set(mappedBaseColumns.concat(["content"])); + +export class MykiCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.nickname, "--"); + cipher.notes = this.getValueOrDefault(value.additionalInfo); + + if (value.url !== undefined) { + // Accounts + cipher.login.uris = this.makeUriArray(value.url); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.totp = this.getValueOrDefault(value.twofaSecret); + + this.importUnmappedFields(cipher, value, _mappedUserAccountColumns); + } else if (value.authToken !== undefined) { + // TwoFA + cipher.login.totp = this.getValueOrDefault(value.authToken); + + this.importUnmappedFields(cipher, value, _mappedTwoFaColumns); + } else if (value.cardNumber !== undefined) { + // Cards + cipher.card = new CardView(); + cipher.type = CipherType.Card; + cipher.card.cardholderName = this.getValueOrDefault(value.cardName); + cipher.card.number = this.getValueOrDefault(value.cardNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.expMonth = this.getValueOrDefault(value.exp_month); + cipher.card.expYear = this.getValueOrDefault(value.exp_year); + cipher.card.code = this.getValueOrDefault(value.cvv); + + this.importUnmappedFields(cipher, value, _mappedCreditCardColumns); + } else if (value.firstName !== undefined) { + // Identities + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + cipher.identity.title = this.getValueOrDefault(value.title); + cipher.identity.firstName = this.getValueOrDefault(value.firstName); + cipher.identity.middleName = this.getValueOrDefault(value.middleName); + cipher.identity.lastName = this.getValueOrDefault(value.lastName); + cipher.identity.phone = this.getValueOrDefault(value.number); + cipher.identity.email = this.getValueOrDefault(value.email); + cipher.identity.address1 = this.getValueOrDefault(value.firstAddressLine); + cipher.identity.address2 = this.getValueOrDefault(value.secondAddressLine); + cipher.identity.city = this.getValueOrDefault(value.city); + cipher.identity.country = this.getValueOrDefault(value.country); + cipher.identity.postalCode = this.getValueOrDefault(value.zipCode); + + this.importUnmappedFields(cipher, value, _mappedIdentityColumns); + } else if (value.idType !== undefined) { + // IdCards + + cipher.identity = new IdentityView(); + cipher.type = CipherType.Identity; + this.processFullName(cipher, value.idName); + cipher.identity.country = this.getValueOrDefault(value.idCountry); + + switch (value.idType) { + // case "Driver's License": + // case "ID Card": + // case "Outdoor License": + // case "Software License": + // case "Tax Number": + // case "Bank Account": + // case "Insurance Card": + // case "Health Card": + // case "Membership": + // case "Database": + // case "Reward Program": + // case "Tour Visa": + case "Passport": + cipher.identity.passportNumber = value.idNumber; + break; + case "Social Security": + cipher.identity.ssn = value.idNumber; + break; + default: + cipher.identity.licenseNumber = value.idNumber; + break; + } + + this.importUnmappedFields(cipher, value, _mappedIdCardColumns); + } else if (value.content !== undefined) { + // Notes + cipher.secureNote = new SecureNoteView(); + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + cipher.notes = this.getValueOrDefault(value.content); + + this.importUnmappedFields(cipher, value, _mappedUserNoteColumns); + } else { + return; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set) { + const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x)); + unmappedFields.forEach((key) => { + const item = row as any; + this.processKvp(cipher, key, item[key]); + }); + } +} diff --git a/jslib/common/src/importers/nordpassCsvImporter.ts b/jslib/common/src/importers/nordpassCsvImporter.ts new file mode 100644 index 00000000..225e7ec2 --- /dev/null +++ b/jslib/common/src/importers/nordpassCsvImporter.ts @@ -0,0 +1,127 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { LoginView } from "../models/view/loginView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +type nodePassCsvParsed = { + name: string; + url: string; + username: string; + password: string; + note: string; + cardholdername: string; + cardnumber: string; + cvc: string; + expirydate: string; + zipcode: string; + folder: string; + full_name: string; + phone_number: string; + email: string; + address1: string; + address2: string; + city: string; + country: string; + state: string; +}; + +export class NordPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results: nodePassCsvParsed[] = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((record) => { + const recordType = this.evaluateType(record); + if (recordType === undefined) { + return; + } + + if (!this.organization) { + this.processFolder(result, record.folder); + } + + const cipher = new CipherView(); + cipher.name = this.getValueOrDefault(record.name, "--"); + cipher.notes = this.getValueOrDefault(record.note); + + switch (recordType) { + case CipherType.Login: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.username = this.getValueOrDefault(record.username); + cipher.login.password = this.getValueOrDefault(record.password); + cipher.login.uris = this.makeUriArray(record.url); + break; + case CipherType.Card: + cipher.type = CipherType.Card; + cipher.card.cardholderName = this.getValueOrDefault(record.cardholdername); + cipher.card.number = this.getValueOrDefault(record.cardnumber); + cipher.card.code = this.getValueOrDefault(record.cvc); + cipher.card.brand = this.getCardBrand(cipher.card.number); + this.setCardExpiration(cipher, record.expirydate); + break; + + case CipherType.Identity: + cipher.type = CipherType.Identity; + + this.processFullName(cipher, this.getValueOrDefault(record.full_name)); + cipher.identity.address1 = this.getValueOrDefault(record.address1); + cipher.identity.address2 = this.getValueOrDefault(record.address2); + cipher.identity.city = this.getValueOrDefault(record.city); + cipher.identity.state = this.getValueOrDefault(record.state); + cipher.identity.postalCode = this.getValueOrDefault(record.zipcode); + cipher.identity.country = this.getValueOrDefault(record.country); + if (cipher.identity.country != null) { + cipher.identity.country = cipher.identity.country.toUpperCase(); + } + cipher.identity.email = this.getValueOrDefault(record.email); + cipher.identity.phone = this.getValueOrDefault(record.phone_number); + break; + case CipherType.SecureNote: + cipher.type = CipherType.SecureNote; + cipher.secureNote.type = SecureNoteType.Generic; + break; + default: + break; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private evaluateType(record: nodePassCsvParsed): CipherType { + if (!this.isNullOrWhitespace(record.username)) { + return CipherType.Login; + } + + if (!this.isNullOrWhitespace(record.cardnumber)) { + return CipherType.Card; + } + + if (!this.isNullOrWhitespace(record.full_name)) { + return CipherType.Identity; + } + + if (!this.isNullOrWhitespace(record.note)) { + return CipherType.SecureNote; + } + + return undefined; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts b/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts new file mode 100644 index 00000000..560f5c01 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/cipherImportContext.ts @@ -0,0 +1,8 @@ +import { CipherView } from "../../models/view/cipherView"; + +export class CipherImportContext { + lowerProperty: string; + constructor(public importRecord: any, public property: string, public cipher: CipherView) { + this.lowerProperty = property.toLowerCase(); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts new file mode 100644 index 00000000..f261140e --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepassword1PifImporter.ts @@ -0,0 +1,276 @@ +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { PasswordHistoryView } from "../../models/view/passwordHistoryView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +export class OnePassword1PifImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + data.split(this.newLineRegex).forEach((line) => { + if (this.isNullOrWhitespace(line) || line[0] !== "{") { + return; + } + const item = JSON.parse(line); + if (item.trashed === true) { + return; + } + const cipher = this.initLoginCipher(); + + if (this.isNullOrWhitespace(item.hmac)) { + this.processStandardItem(item, cipher); + } else { + this.processWinOpVaultItem(item, cipher); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processWinOpVaultItem(item: any, cipher: CipherView) { + if (item.overview != null) { + cipher.name = this.getValueOrDefault(item.overview.title); + if (item.overview.URLs != null) { + const urls: string[] = []; + item.overview.URLs.forEach((url: any) => { + if (!this.isNullOrWhitespace(url.u)) { + urls.push(url.u); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + } + + if (item.details != null) { + if (item.details.passwordHistory != null) { + this.parsePasswordHistory(item.details.passwordHistory, cipher); + } + if ( + !this.isNullOrWhitespace(item.details.ccnum) || + !this.isNullOrWhitespace(item.details.cvv) + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } else if ( + !this.isNullOrWhitespace(item.details.firstname) || + !this.isNullOrWhitespace(item.details.address1) + ) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } + if (cipher.type === CipherType.Login && !this.isNullOrWhitespace(item.details.password)) { + cipher.login.password = item.details.password; + } + if (!this.isNullOrWhitespace(item.details.notesPlain)) { + cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + if (item.details.fields != null) { + this.parseFields(item.details.fields, cipher, "designation", "value", "name"); + } + if (item.details.sections != null) { + item.details.sections.forEach((section: any) => { + if (section.fields != null) { + this.parseFields(section.fields, cipher, "n", "v", "t"); + } + }); + } + } + } + + private processStandardItem(item: any, cipher: CipherView) { + cipher.favorite = item.openContents && item.openContents.faveIndex ? true : false; + cipher.name = this.getValueOrDefault(item.title); + + if (item.typeName === "securenotes.SecureNote") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } else if (item.typeName === "wallet.financial.CreditCard") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } else if (item.typeName === "identities.Identity") { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } else { + cipher.login.uris = this.makeUriArray(item.location); + } + + if (item.secureContents != null) { + if (item.secureContents.passwordHistory != null) { + this.parsePasswordHistory(item.secureContents.passwordHistory, cipher); + } + if (!this.isNullOrWhitespace(item.secureContents.notesPlain)) { + cipher.notes = item.secureContents.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + if (cipher.type === CipherType.Login) { + if (!this.isNullOrWhitespace(item.secureContents.password)) { + cipher.login.password = item.secureContents.password; + } + if (item.secureContents.URLs != null) { + const urls: string[] = []; + item.secureContents.URLs.forEach((u: any) => { + if (!this.isNullOrWhitespace(u.url)) { + urls.push(u.url); + } + }); + if (urls.length > 0) { + cipher.login.uris = this.makeUriArray(urls); + } + } + } + if (item.secureContents.fields != null) { + this.parseFields(item.secureContents.fields, cipher, "designation", "value", "name"); + } + if (item.secureContents.sections != null) { + item.secureContents.sections.forEach((section: any) => { + if (section.fields != null) { + this.parseFields(section.fields, cipher, "n", "v", "t"); + } + }); + } + } + } + + private parsePasswordHistory(items: any[], cipher: CipherView) { + const maxSize = items.length > 5 ? 5 : items.length; + cipher.passwordHistory = items + .filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null) + .sort((a, b) => b.time - a.time) + .slice(0, maxSize) + .map((h: any) => { + const ph = new PasswordHistoryView(); + ph.password = h.value; + ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000); + return ph; + }); + } + + private parseFields( + fields: any[], + cipher: CipherView, + designationKey: string, + valueKey: string, + nameKey: string + ) { + fields.forEach((field: any) => { + if (field[valueKey] == null || field[valueKey].toString().trim() === "") { + return; + } + + // TODO: when date FieldType exists, store this as a date field type instead of formatted Text if k is 'date' + const fieldValue = + field.k === "date" + ? new Date(field[valueKey] * 1000).toUTCString() + : field[valueKey].toString(); + const fieldDesignation = + field[designationKey] != null ? field[designationKey].toString() : null; + + if (cipher.type === CipherType.Login) { + if (this.isNullOrWhitespace(cipher.login.username) && fieldDesignation === "username") { + cipher.login.username = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.login.password) && + fieldDesignation === "password" + ) { + cipher.login.password = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.login.totp) && + fieldDesignation != null && + fieldDesignation.startsWith("TOTP_") + ) { + cipher.login.totp = fieldValue; + return; + } + } else if (cipher.type === CipherType.Card) { + if (this.isNullOrWhitespace(cipher.card.number) && fieldDesignation === "ccnum") { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + return; + } else if (this.isNullOrWhitespace(cipher.card.code) && fieldDesignation === "cvv") { + cipher.card.code = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.card.cardholderName) && + fieldDesignation === "cardholder" + ) { + cipher.card.cardholderName = fieldValue; + return; + } else if ( + this.isNullOrWhitespace(cipher.card.expiration) && + fieldDesignation === "expiry" && + fieldValue.length === 6 + ) { + cipher.card.expMonth = (fieldValue as string).substr(4, 2); + if (cipher.card.expMonth[0] === "0") { + cipher.card.expMonth = cipher.card.expMonth.substr(1, 1); + } + cipher.card.expYear = (fieldValue as string).substr(0, 4); + return; + } else if (fieldDesignation === "type") { + // Skip since brand was determined from number above + return; + } + } else if (cipher.type === CipherType.Identity) { + const identity = cipher.identity; + if (this.isNullOrWhitespace(identity.firstName) && fieldDesignation === "firstname") { + identity.firstName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.lastName) && fieldDesignation === "lastname") { + identity.lastName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.middleName) && fieldDesignation === "initial") { + identity.middleName = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.phone) && fieldDesignation === "defphone") { + identity.phone = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.company) && fieldDesignation === "company") { + identity.company = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.email) && fieldDesignation === "email") { + identity.email = fieldValue; + return; + } else if (this.isNullOrWhitespace(identity.username) && fieldDesignation === "username") { + identity.username = fieldValue; + return; + } else if (fieldDesignation === "address") { + // fieldValue is an object casted into a string, so access the plain value instead + const { street, city, country, zip } = field[valueKey]; + identity.address1 = this.getValueOrDefault(street); + identity.city = this.getValueOrDefault(city); + if (!this.isNullOrWhitespace(country)) { + identity.country = country.toUpperCase(); + } + identity.postalCode = this.getValueOrDefault(zip); + return; + } + } + + const fieldName = this.isNullOrWhitespace(field[nameKey]) ? "no_name" : field[nameKey]; + if ( + fieldName === "password" && + cipher.passwordHistory != null && + cipher.passwordHistory.some((h) => h.password === fieldValue) + ) { + return; + } + + const fieldType = field.k === "concealed" ? FieldType.Hidden : FieldType.Text; + this.processKvp(cipher, fieldName, fieldValue, fieldType); + }); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts new file mode 100644 index 00000000..67f4bb25 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepassword1PuxImporter.ts @@ -0,0 +1,637 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { SecureNoteType } from "../../enums/secureNoteType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { PasswordHistoryView } from "../../models/view/passwordHistoryView"; +import { SecureNoteView } from "../../models/view/secureNoteView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { + CategoryEnum, + Details, + ExportData, + FieldsEntity, + Item, + LoginFieldTypeEnum, + Overview, + PasswordHistoryEntity, + SectionsEntity, + UrlsEntity, + Value, + VaultsEntity, +} from "./types/onepassword1PuxImporterTypes"; + +export class OnePassword1PuxImporter extends BaseImporter implements Importer { + result = new ImportResult(); + + parse(data: string): Promise { + const exportData: ExportData = JSON.parse(data); + + const account = exportData.accounts[0]; + // TODO Add handling of multiple vaults + // const personalVaults = account.vaults[0].filter((v) => v.attrs.type === VaultAttributeTypeEnum.Personal); + account.vaults.forEach((vault: VaultsEntity) => { + vault.items.forEach((item: Item) => { + if (item.trashed === true) { + return; + } + + const cipher = this.initLoginCipher(); + + const category = item.categoryUuid as CategoryEnum; + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.Password: + case CategoryEnum.WirelessRouter: + case CategoryEnum.Server: + case CategoryEnum.API_Credential: + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + break; + case CategoryEnum.CreditCard: + case CategoryEnum.BankAccount: + cipher.type = CipherType.Card; + cipher.card = new CardView(); + break; + case CategoryEnum.SecureNote: + case CategoryEnum.SoftwareLicense: + case CategoryEnum.EmailAccount: + case CategoryEnum.MedicalRecord: + // case CategoryEnum.Document: + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + case CategoryEnum.Identity: + case CategoryEnum.DriversLicense: + case CategoryEnum.OutdoorLicense: + case CategoryEnum.Membership: + case CategoryEnum.Passport: + case CategoryEnum.RewardsProgram: + case CategoryEnum.SocialSecurityNumber: + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + break; + default: + break; + } + + cipher.favorite = item.favIndex === 1 ? true : false; + + this.processOverview(item.overview, cipher); + + this.processLoginFields(item, cipher); + + this.processDetails(category, item.details, cipher); + + this.parsePasswordHistory(item.details.passwordHistory, cipher); + + this.processSections(category, item.details.sections, cipher); + + if (!this.isNullOrWhitespace(item.details.notesPlain)) { + cipher.notes = item.details.notesPlain.split(this.newLineRegex).join("\n") + "\n"; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + this.result.ciphers.push(cipher); + }); + }); + + if (this.organization) { + this.moveFoldersToCollections(this.result); + } + + this.result.success = true; + return Promise.resolve(this.result); + } + + private processOverview(overview: Overview, cipher: CipherView) { + if (overview == null) { + return; + } + + cipher.name = this.getValueOrDefault(overview.title); + + if (overview.urls != null) { + const urls: string[] = []; + overview.urls.forEach((url: UrlsEntity) => { + if (!this.isNullOrWhitespace(url.url)) { + urls.push(url.url); + } + }); + cipher.login.uris = this.makeUriArray(urls); + } + + if (overview.tags != null && overview.tags.length > 0) { + const folderName = this.capitalize(overview.tags[0]); + this.processFolder(this.result, folderName); + } + } + + private capitalize(inputString: string): string { + return inputString.trim().replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase())); + } + + private processLoginFields(item: Item, cipher: CipherView) { + if (item.details == null) { + return; + } + + if (item.details.loginFields == null || item.details.loginFields.length === 0) { + return; + } + + item.details.loginFields.forEach((loginField) => { + if (loginField.designation === "username" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.username = loginField.value; + return; + } + + if (loginField.designation === "password" && loginField.value !== "") { + cipher.type = CipherType.Login; + cipher.login.password = loginField.value; + return; + } + + let fieldValue = loginField.value; + let fieldType: FieldType = FieldType.Text; + switch (loginField.fieldType) { + case LoginFieldTypeEnum.Password: + fieldType = FieldType.Hidden; + break; + case LoginFieldTypeEnum.CheckBox: + fieldValue = loginField.value !== "" ? "true" : "false"; + fieldType = FieldType.Boolean; + break; + default: + break; + } + this.processKvp(cipher, loginField.name, fieldValue, fieldType); + }); + } + + private processDetails(category: CategoryEnum, details: Details, cipher: CipherView) { + if (category !== CategoryEnum.Password) { + return; + } + + if (details == null) { + return; + } + cipher.login.password = details.password; + } + + private processSections(category: CategoryEnum, sections: SectionsEntity[], cipher: CipherView) { + if (sections == null || sections.length === 0) { + return; + } + + sections.forEach((section: SectionsEntity) => { + if (section.fields == null) { + return; + } + + this.parseSectionFields(category, section.fields, cipher); + }); + } + + private parseSectionFields(category: CategoryEnum, fields: FieldsEntity[], cipher: CipherView) { + fields.forEach((field: FieldsEntity) => { + const valueKey = Object.keys(field.value)[0]; + const anyField = field as any; + + if ( + anyField.value == null || + anyField.value[valueKey] == null || + anyField.value[valueKey] === "" + ) { + return; + } + + const fieldName = this.getFieldName(field.id, field.title); + const fieldValue = this.extractValue(field.value, valueKey); + + if (cipher.type === CipherType.Login) { + if (this.fillLogin(field, fieldValue, cipher)) { + return; + } + + switch (category) { + case CategoryEnum.Login: + case CategoryEnum.Database: + case CategoryEnum.EmailAccount: + case CategoryEnum.WirelessRouter: + break; + + case CategoryEnum.Server: + if (this.isNullOrWhitespace(cipher.login.uri) && field.id === "url") { + cipher.login.uris = this.makeUriArray(fieldValue); + return; + } + break; + + case CategoryEnum.API_Credential: + if (this.fillApiCredentials(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } else if (cipher.type === CipherType.Card) { + if (this.fillCreditCard(field, fieldValue, cipher)) { + return; + } + + if (category === CategoryEnum.BankAccount) { + if (this.fillBankAccount(field, fieldValue, cipher)) { + return; + } + } + } else if (cipher.type === CipherType.Identity) { + if (this.fillIdentity(field, fieldValue, cipher, valueKey)) { + return; + } + if (valueKey === "address") { + // fieldValue is an object casted into a string, so access the plain value instead + const { street, city, country, zip, state } = field.value.address; + cipher.identity.address1 = this.getValueOrDefault(street); + cipher.identity.city = this.getValueOrDefault(city); + if (!this.isNullOrWhitespace(country)) { + cipher.identity.country = country.toUpperCase(); + } + cipher.identity.postalCode = this.getValueOrDefault(zip); + cipher.identity.state = this.getValueOrDefault(state); + return; + } + + switch (category) { + case CategoryEnum.Identity: + break; + case CategoryEnum.DriversLicense: + if (this.fillDriversLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.OutdoorLicense: + if (this.fillOutdoorLicense(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Membership: + if (this.fillMembership(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.Passport: + if (this.fillPassport(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.RewardsProgram: + if (this.fillRewardsProgram(field, fieldValue, cipher)) { + return; + } + break; + case CategoryEnum.SocialSecurityNumber: + if (this.fillSSN(field, fieldValue, cipher)) { + return; + } + break; + default: + break; + } + } + + if (valueKey === "email") { + // fieldValue is an object casted into a string, so access the plain value instead + const { email_address, provider } = field.value.email; + this.processKvp(cipher, fieldName, email_address, FieldType.Text); + this.processKvp(cipher, "provider", provider, FieldType.Text); + return; + } + + // Do not include a password field if it's already in the history + if ( + field.title === "password" && + cipher.passwordHistory != null && + cipher.passwordHistory.some((h) => h.password === fieldValue) + ) { + return; + } + + // TODO ?? If one of the fields is marked as guarded, then activate Password-Reprompt for the entire item + if (field.guarded && cipher.reprompt === CipherRepromptType.None) { + cipher.reprompt = CipherRepromptType.Password; + } + + const fieldType = valueKey === "concealed" ? FieldType.Hidden : FieldType.Text; + this.processKvp(cipher, fieldName, fieldValue, fieldType); + }); + } + + private getFieldName(id: string, title: string): string { + if (this.isNullOrWhitespace(title)) { + return id; + } + + // Naive approach of checking if the fields id is usable + if (id.length > 25 && RegExp(/[0-9]{2}[A-Z]{2}/, "i").test(id)) { + return title; + } + return id; + } + + private extractValue(value: Value, valueKey: string): string { + if (valueKey === "date") { + return new Date(value.date * 1000).toUTCString(); + } + + if (valueKey === "monthYear") { + return value.monthYear.toString(); + } + + return (value as any)[valueKey]; + } + + private fillLogin(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.username) && fieldName === "username") { + cipher.login.username = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "password") { + cipher.login.password = fieldValue; + return true; + } + + if ( + this.isNullOrWhitespace(cipher.login.totp) && + field.id != null && + field.id.startsWith("TOTP_") + ) { + cipher.login.totp = fieldValue; + return true; + } + + return false; + } + + private fillApiCredentials(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + const fieldName = this.getFieldName(field.id, field.title); + + if (this.isNullOrWhitespace(cipher.login.password) && fieldName === "credential") { + cipher.login.password = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.login.uri) && fieldName === "hostname") { + cipher.login.uris = this.makeUriArray(fieldValue); + return true; + } + + return false; + } + + private fillCreditCard(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.number) && field.id === "ccnum") { + cipher.card.number = fieldValue; + cipher.card.brand = this.getCardBrand(fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.card.code) && field.id === "cvv") { + cipher.card.code = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "cardholder") { + cipher.card.cardholderName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.card.expiration) && field.id === "expiry") { + const monthYear: string = fieldValue.toString().trim(); + cipher.card.expMonth = monthYear.substring(4, 6); + if (cipher.card.expMonth[0] === "0") { + cipher.card.expMonth = cipher.card.expMonth.substring(1, 2); + } + cipher.card.expYear = monthYear.substring(0, 4); + return true; + } + + if (field.id === "type") { + // Skip since brand was determined from number above + return true; + } + + return false; + } + + private fillBankAccount(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.card.cardholderName) && field.id === "owner") { + cipher.card.cardholderName = fieldValue; + return true; + } + + return false; + } + + private fillIdentity( + field: FieldsEntity, + fieldValue: string, + cipher: CipherView, + valueKey: string + ): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") { + cipher.identity.firstName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.lastName) && field.id === "lastname") { + cipher.identity.lastName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.middleName) && field.id === "initial") { + cipher.identity.middleName = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "defphone") { + cipher.identity.phone = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.email)) { + if (valueKey === "email") { + const { email_address, provider } = field.value.email; + cipher.identity.email = this.getValueOrDefault(email_address); + this.processKvp(cipher, "provider", provider, FieldType.Text); + return true; + } + + if (field.id === "email") { + cipher.identity.email = fieldValue; + return true; + } + } + + if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") { + cipher.identity.username = fieldValue; + return true; + } + return false; + } + + private fillDriversLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.address1) && field.id === "address") { + cipher.identity.address1 = fieldValue; + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.licenseNumber) && field.id === "number") { + cipher.identity.licenseNumber = fieldValue; + return true; + } + + return false; + } + + private fillOutdoorLicense(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO ISO code + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.state) && field.id === "state") { + cipher.identity.state = fieldValue; + return true; + } + + return false; + } + + private fillMembership(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "org_name") { + cipher.identity.company = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.phone) && field.id === "phone") { + cipher.identity.phone = fieldValue; + return true; + } + + return false; + } + + private fillPassport(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "fullname") { + this.processFullName(cipher, fieldValue); + return true; + } + + // TODO Iso + if (this.isNullOrWhitespace(cipher.identity.country) && field.id === "issuing_country") { + cipher.identity.country = fieldValue; + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.passportNumber) && field.id === "number") { + cipher.identity.passportNumber = fieldValue; + return true; + } + + return false; + } + + private fillRewardsProgram(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "member_name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.company) && field.id === "company_name") { + cipher.identity.company = fieldValue; + return true; + } + + return false; + } + + private fillSSN(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean { + if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "name") { + this.processFullName(cipher, fieldValue); + return true; + } + + if (this.isNullOrWhitespace(cipher.identity.ssn) && field.id === "number") { + cipher.identity.ssn = fieldValue; + return true; + } + + return false; + } + + private parsePasswordHistory(historyItems: PasswordHistoryEntity[], cipher: CipherView) { + if (historyItems == null || historyItems.length === 0) { + return; + } + + const maxSize = historyItems.length > 5 ? 5 : historyItems.length; + cipher.passwordHistory = historyItems + .filter((h: any) => !this.isNullOrWhitespace(h.value) && h.time != null) + .sort((a, b) => b.time - a.time) + .slice(0, maxSize) + .map((h: any) => { + const ph = new PasswordHistoryView(); + ph.password = h.value; + ph.lastUsedDate = new Date(("" + h.time).length >= 13 ? h.time : h.time * 1000); + return ph; + }); + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts new file mode 100644 index 00000000..d28bdccc --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordCsvImporter.ts @@ -0,0 +1,383 @@ +import { CipherType } from "../../enums/cipherType"; +import { FieldType } from "../../enums/fieldType"; +import { ImportResult } from "../../models/domain/importResult"; +import { CipherView } from "../../models/view/cipherView"; +import { BaseImporter } from "../baseImporter"; +import { Importer } from "../importer"; + +import { CipherImportContext } from "./cipherImportContext"; + +export const IgnoredProperties = [ + "ainfo", + "autosubmit", + "notesplain", + "ps", + "scope", + "tags", + "title", + "uuid", + "notes", +]; + +export abstract class OnePasswordCsvImporter extends BaseImporter implements Importer { + protected loginPropertyParsers = [ + this.setLoginUsername, + this.setLoginPassword, + this.setLoginUris, + ]; + protected creditCardPropertyParsers = [ + this.setCreditCardNumber, + this.setCreditCardVerification, + this.setCreditCardCardholderName, + this.setCreditCardExpiry, + ]; + protected identityPropertyParsers = [ + this.setIdentityFirstName, + this.setIdentityInitial, + this.setIdentityLastName, + this.setIdentityUserName, + this.setIdentityEmail, + this.setIdentityPhone, + this.setIdentityCompany, + ]; + + abstract setCipherType(value: any, cipher: CipherView): void; + + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true, { + quoteChar: '"', + escapeChar: "\\", + }); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (this.isNullOrWhitespace(this.getProp(value, "title"))) { + return; + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(this.getProp(value, "title"), "--"); + + this.setNotes(value, cipher); + + this.setCipherType(value, cipher); + + let altUsername: string = null; + for (const property in value) { + // eslint-disable-next-line + if (!value.hasOwnProperty(property) || this.isNullOrWhitespace(value[property])) { + continue; + } + + const context = new CipherImportContext(value, property, cipher); + if (cipher.type === CipherType.Login && this.setKnownLoginValue(context)) { + continue; + } else if (cipher.type === CipherType.Card && this.setKnownCreditCardValue(context)) { + continue; + } else if (cipher.type === CipherType.Identity && this.setKnownIdentityValue(context)) { + continue; + } + + altUsername = this.setUnknownValue(context, altUsername); + } + + if ( + cipher.type === CipherType.Login && + !this.isNullOrWhitespace(altUsername) && + this.isNullOrWhitespace(cipher.login.username) && + altUsername.indexOf("://") === -1 + ) { + cipher.login.username = altUsername; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } + + protected getProp(obj: any, name: string): any { + const lowerObj = Object.entries(obj).reduce((agg: any, entry: [string, any]) => { + agg[entry[0].toLowerCase()] = entry[1]; + return agg; + }, {}); + return lowerObj[name.toLowerCase()]; + } + + protected getPropByRegexp(obj: any, regexp: RegExp): any { + const matchingKeys = Object.keys(obj).reduce((agg: string[], key: string) => { + if (key.match(regexp)) { + agg.push(key); + } + return agg; + }, []); + if (matchingKeys.length === 0) { + return null; + } else { + return obj[matchingKeys[0]]; + } + } + + protected getPropIncluding(obj: any, name: string): any { + const includesMap = Object.keys(obj).reduce((agg: string[], entry: string) => { + if (entry.toLowerCase().includes(name.toLowerCase())) { + agg.push(entry); + } + return agg; + }, []); + if (includesMap.length === 0) { + return null; + } else { + return obj[includesMap[0]]; + } + } + + protected setNotes(importRecord: any, cipher: CipherView) { + cipher.notes = + this.getValueOrDefault(this.getProp(importRecord, "notesPlain"), "") + + "\n" + + this.getValueOrDefault(this.getProp(importRecord, "notes"), "") + + "\n"; + cipher.notes.trim(); + } + + protected setKnownLoginValue(context: CipherImportContext): boolean { + return this.loginPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownCreditCardValue(context: CipherImportContext): boolean { + return this.creditCardPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setKnownIdentityValue(context: CipherImportContext): boolean { + return this.identityPropertyParsers.reduce((agg: boolean, func) => { + if (!agg) { + agg = func.bind(this)(context); + } + return agg; + }, false); + } + + protected setUnknownValue(context: CipherImportContext, altUsername: string): string { + if ( + IgnoredProperties.indexOf(context.lowerProperty) === -1 && + !context.lowerProperty.startsWith("section:") && + !context.lowerProperty.startsWith("section ") + ) { + if (altUsername == null && context.lowerProperty === "email") { + return context.importRecord[context.property]; + } else if ( + context.lowerProperty === "created date" || + context.lowerProperty === "modified date" + ) { + const readableDate = new Date( + parseInt(context.importRecord[context.property], 10) * 1000 + ).toUTCString(); + this.processKvp(context.cipher, "1Password " + context.property, readableDate); + return null; + } + if ( + context.lowerProperty.includes("password") || + context.lowerProperty.includes("key") || + context.lowerProperty.includes("secret") + ) { + this.processKvp( + context.cipher, + context.property, + context.importRecord[context.property], + FieldType.Hidden + ); + } else { + this.processKvp(context.cipher, context.property, context.importRecord[context.property]); + } + } + return null; + } + + protected setIdentityFirstName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.firstName) && + context.lowerProperty.includes("first name") + ) { + context.cipher.identity.firstName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityInitial(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.middleName) && + context.lowerProperty.includes("initial") + ) { + context.cipher.identity.middleName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityLastName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.lastName) && + context.lowerProperty.includes("last name") + ) { + context.cipher.identity.lastName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityUserName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.username) && + context.lowerProperty.includes("username") + ) { + context.cipher.identity.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityCompany(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.company) && + context.lowerProperty.includes("company") + ) { + context.cipher.identity.company = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityPhone(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.phone) && + context.lowerProperty.includes("default phone") + ) { + context.cipher.identity.phone = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setIdentityEmail(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.identity.email) && + context.lowerProperty.includes("email") + ) { + context.cipher.identity.email = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardNumber(context: CipherImportContext): boolean { + if ( + this.isNullOrWhitespace(context.cipher.card.number) && + context.lowerProperty.includes("number") + ) { + context.cipher.card.number = context.importRecord[context.property]; + context.cipher.card.brand = this.getCardBrand(context.cipher.card.number); + return true; + } + return false; + } + + protected setCreditCardVerification(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.code) && + context.lowerProperty.includes("verification number") + ) { + context.cipher.card.code = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardCardholderName(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.cardholderName) && + context.lowerProperty.includes("cardholder name") + ) { + context.cipher.card.cardholderName = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setCreditCardExpiry(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.expiration) && + context.lowerProperty.includes("expiry date") && + context.importRecord[context.property].length === 7 + ) { + context.cipher.card.expMonth = (context.importRecord[context.property] as string).substr( + 0, + 2 + ); + if (context.cipher.card.expMonth[0] === "0") { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = (context.importRecord[context.property] as string).substr(3, 4); + return true; + } + return false; + } + + protected setLoginPassword(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.login.password) && + context.lowerProperty === "password" + ) { + context.cipher.login.password = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUsername(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.login.username) && + context.lowerProperty === "username" + ) { + context.cipher.login.username = context.importRecord[context.property]; + return true; + } + return false; + } + + protected setLoginUris(context: CipherImportContext) { + if ( + (context.cipher.login.uris == null || context.cipher.login.uris.length === 0) && + context.lowerProperty === "urls" + ) { + const urls = context.importRecord[context.property].split(this.newLineRegex); + context.cipher.login.uris = this.makeUriArray(urls); + return true; + } else if (context.lowerProperty === "url") { + if (context.cipher.login.uris == null) { + context.cipher.login.uris = []; + } + context.cipher.login.uris.concat(this.makeUriArray(context.importRecord[context.property])); + return true; + } + return false; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts new file mode 100644 index 00000000..2135c80a --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordMacCsvImporter.ts @@ -0,0 +1,31 @@ +import { CipherType } from "../../enums/cipherType"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { Importer } from "../importer"; + +import { IgnoredProperties, OnePasswordCsvImporter } from "./onepasswordCsvImporter"; + +export class OnePasswordMacCsvImporter extends OnePasswordCsvImporter implements Importer { + setCipherType(value: any, cipher: CipherView) { + const onePassType = this.getValueOrDefault(this.getProp(value, "type"), "Login"); + switch (onePassType) { + case "Credit Card": + cipher.type = CipherType.Card; + cipher.card = new CardView(); + IgnoredProperties.push("type"); + break; + case "Identity": + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + IgnoredProperties.push("type"); + break; + case "Login": + case "Secure Note": + IgnoredProperties.push("type"); + break; + default: + break; + } + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts b/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts new file mode 100644 index 00000000..3b3d6454 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/onepasswordWinCsvImporter.ts @@ -0,0 +1,63 @@ +import { CipherType } from "../../enums/cipherType"; +import { CardView } from "../../models/view/cardView"; +import { CipherView } from "../../models/view/cipherView"; +import { IdentityView } from "../../models/view/identityView"; +import { LoginView } from "../../models/view/loginView"; +import { Importer } from "../importer"; + +import { CipherImportContext } from "./cipherImportContext"; +import { OnePasswordCsvImporter } from "./onepasswordCsvImporter"; + +export class OnePasswordWinCsvImporter extends OnePasswordCsvImporter implements Importer { + constructor() { + super(); + this.identityPropertyParsers.push(this.setIdentityAddress); + } + + setCipherType(value: any, cipher: CipherView) { + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + + if ( + !this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: number/i)) && + !this.isNullOrWhitespace(this.getPropByRegexp(value, /\d+: expiry date/i)) + ) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + } + + if ( + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: first name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: initial/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /name \d+: last name/i)) || + !this.isNullOrWhitespace(this.getPropByRegexp(value, /internet \d+: email/i)) + ) { + cipher.type = CipherType.Identity; + cipher.identity = new IdentityView(); + } + } + + setIdentityAddress(context: CipherImportContext) { + if (context.lowerProperty.match(/address \d+: address/i)) { + this.processKvp(context.cipher, "address", context.importRecord[context.property]); + return true; + } + return false; + } + + setCreditCardExpiry(context: CipherImportContext) { + if ( + this.isNullOrWhitespace(context.cipher.card.expiration) && + context.lowerProperty.includes("expiry date") + ) { + const expSplit = (context.importRecord[context.property] as string).split("/"); + context.cipher.card.expMonth = expSplit[0]; + if (context.cipher.card.expMonth[0] === "0" && context.cipher.card.expMonth.length === 2) { + context.cipher.card.expMonth = context.cipher.card.expMonth.substr(1, 1); + } + context.cipher.card.expYear = expSplit[2].length > 4 ? expSplit[2].substr(0, 4) : expSplit[2]; + return true; + } + return false; + } +} diff --git a/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts b/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts new file mode 100644 index 00000000..46ee0619 --- /dev/null +++ b/jslib/common/src/importers/onepasswordImporters/types/onepassword1PuxImporterTypes.ts @@ -0,0 +1,160 @@ +export interface ExportData { + accounts?: AccountsEntity[] | null; +} +export interface AccountsEntity { + attrs: AccountAttributes; + vaults?: VaultsEntity[] | null; +} +export interface AccountAttributes { + accountName: string; + name: string; + avatar: string; + email: string; + uuid: string; + domain: string; +} +export interface VaultsEntity { + attrs: VaultAttributes; + items?: Item[] | null; +} +export interface VaultAttributes { + uuid: string; + desc: string; + avatar: string; + name: string; + type: string; +} + +export enum CategoryEnum { + Login = "001", + CreditCard = "002", + SecureNote = "003", + Identity = "004", + Password = "005", + Document = "006", + SoftwareLicense = "100", + BankAccount = "101", + Database = "102", + DriversLicense = "103", + OutdoorLicense = "104", + Membership = "105", + Passport = "106", + RewardsProgram = "107", + SocialSecurityNumber = "108", + WirelessRouter = "109", + Server = "110", + EmailAccount = "111", + API_Credential = "112", + MedicalRecord = "113", +} + +export interface Item { + uuid: string; + favIndex: number; + createdAt: number; + updatedAt: number; + trashed?: boolean; + categoryUuid: string; + details: Details; + overview: Overview; +} +export interface Details { + loginFields?: (LoginFieldsEntity | null)[] | null; + notesPlain?: string | null; + sections?: (SectionsEntity | null)[] | null; + passwordHistory?: (PasswordHistoryEntity | null)[] | null; + documentAttributes?: DocumentAttributes | null; + password?: string | null; +} + +export enum LoginFieldTypeEnum { + TextOrHtml = "T", + EmailAddress = "E", + URL = "U", + Number = "N", + Password = "P", + TextArea = "A", + PhoneNumber = "T", + CheckBox = "C", +} +export interface LoginFieldsEntity { + value: string; + id: string; + name: string; + fieldType: LoginFieldTypeEnum | string; + designation?: string | null; +} +export interface SectionsEntity { + title: string; + name?: string | null; + fields?: FieldsEntity[] | null; +} +export interface FieldsEntity { + title: string; + id: string; + value: Value; + indexAtSource: number; + guarded: boolean; + multiline: boolean; + dontGenerate: boolean; + placeholder?: string; + inputTraits: InputTraits; + clipboardFilter?: string | null; +} +export interface Value { + totp?: string | null; + date?: number | null; + string?: string | null; + concealed?: string | null; + email?: Email | null; + phone?: string | null; + menu?: string | null; + gender?: string | null; + monthYear?: number | null; + url?: string | null; + address?: Address | null; + creditCardType?: string | null; + creditCardNumber?: string | null; + reference?: string | null; +} + +export interface Email { + email_address: string; + provider: string; +} + +export interface Address { + street: string; + city: string; + country: string; + zip: string; + state: string; +} +export interface InputTraits { + keyboard: string; + correction: string; + capitalization: string; +} +export interface PasswordHistoryEntity { + value: string; + time: number; +} +export interface DocumentAttributes { + fileName: string; + documentId: string; + decryptedSize: number; +} +export interface Overview { + subtitle: string; + title: string; + url: string; + urls?: UrlsEntity[] | null; + ps?: number | null; + pbe?: number | null; + pgrng?: boolean | null; + tags?: string[] | null; +} +export interface UrlsEntity { + label: string; + url: string; +} diff --git a/jslib/common/src/importers/padlockCsvImporter.ts b/jslib/common/src/importers/padlockCsvImporter.ts new file mode 100644 index 00000000..821391cf --- /dev/null +++ b/jslib/common/src/importers/padlockCsvImporter.ts @@ -0,0 +1,85 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CollectionView } from "../models/view/collectionView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PadlockCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let headers: string[] = null; + results.forEach((value) => { + if (headers == null) { + headers = value.map((v: string) => v); + return; + } + + if (value.length < 2 || value.length !== headers.length) { + return; + } + + if (!this.isNullOrWhitespace(value[1])) { + if (this.organization) { + const tags = (value[1] as string).split(","); + tags.forEach((tag) => { + tag = tag.trim(); + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === tag) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = tag; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else { + const tags = (value[1] as string).split(","); + const tag = tags.length > 0 ? tags[0].trim() : null; + this.processFolder(result, tag); + } + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + + for (let i = 2; i < value.length; i++) { + const header = headers[i].trim().toLowerCase(); + if (this.isNullOrWhitespace(value[i]) || this.isNullOrWhitespace(header)) { + continue; + } + + if (this.usernameFieldNames.indexOf(header) > -1) { + cipher.login.username = value[i]; + } else if (this.passwordFieldNames.indexOf(header) > -1) { + cipher.login.password = value[i]; + } else if (this.uriFieldNames.indexOf(header) > -1) { + cipher.login.uris = this.makeUriArray(value[i]); + } else { + this.processKvp(cipher, headers[i], value[i]); + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passkeepCsvImporter.ts b/jslib/common/src/importers/passkeepCsvImporter.ts new file mode 100644 index 00000000..c93b3a42 --- /dev/null +++ b/jslib/common/src/importers/passkeepCsvImporter.ts @@ -0,0 +1,39 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PassKeepCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + this.processFolder(result, this.getValue("category", value)); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValue("description", value); + cipher.name = this.getValueOrDefault(this.getValue("title", value), "--"); + cipher.login.username = this.getValue("username", value); + cipher.login.password = this.getValue("password", value); + cipher.login.uris = this.makeUriArray(this.getValue("site", value)); + this.processKvp(cipher, "Password 2", this.getValue("password2", value)); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private getValue(key: string, value: any) { + return this.getValueOrDefault(value[key], this.getValueOrDefault(value[" " + key])); + } +} diff --git a/jslib/common/src/importers/passmanJsonImporter.ts b/jslib/common/src/importers/passmanJsonImporter.ts new file mode 100644 index 00000000..f8bc7106 --- /dev/null +++ b/jslib/common/src/importers/passmanJsonImporter.ts @@ -0,0 +1,61 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PassmanJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.length === 0) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((credential: any) => { + if (credential.tags != null && credential.tags.length > 0) { + const folderName = credential.tags[0].text; + this.processFolder(result, folderName); + } + + const cipher = this.initLoginCipher(); + cipher.name = credential.label; + + cipher.login.username = this.getValueOrDefault(credential.username); + if (this.isNullOrWhitespace(cipher.login.username)) { + cipher.login.username = this.getValueOrDefault(credential.email); + } else if (!this.isNullOrWhitespace(credential.email)) { + cipher.notes = "Email: " + credential.email + "\n"; + } + + cipher.login.password = this.getValueOrDefault(credential.password); + cipher.login.uris = this.makeUriArray(credential.url); + cipher.notes += this.getValueOrDefault(credential.description, ""); + if (credential.otp != null) { + cipher.login.totp = this.getValueOrDefault(credential.otp.secret); + } + + if (credential.custom_fields != null) { + credential.custom_fields.forEach((customField: any) => { + switch (customField.field_type) { + case "text": + case "password": + this.processKvp(cipher, customField.label, customField.value); + break; + } + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passpackCsvImporter.ts b/jslib/common/src/importers/passpackCsvImporter.ts new file mode 100644 index 00000000..8efaa3c3 --- /dev/null +++ b/jslib/common/src/importers/passpackCsvImporter.ts @@ -0,0 +1,103 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CollectionView } from "../models/view/collectionView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasspackCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const tagsJson = !this.isNullOrWhitespace(value.Tags) ? JSON.parse(value.Tags) : null; + const tags: string[] = + tagsJson != null && tagsJson.tags != null && tagsJson.tags.length > 0 + ? tagsJson.tags + .map((tagJson: string) => { + try { + const t = JSON.parse(tagJson); + return this.getValueOrDefault(t.tag); + } catch { + // Ignore error + } + return null; + }) + .filter((t: string) => !this.isNullOrWhitespace(t)) + : null; + + if (this.organization && tags != null && tags.length > 0) { + tags.forEach((tag) => { + let addCollection = true; + let collectionIndex = result.collections.length; + + for (let i = 0; i < result.collections.length; i++) { + if (result.collections[i].name === tag) { + addCollection = false; + collectionIndex = i; + break; + } + } + + if (addCollection) { + const collection = new CollectionView(); + collection.name = tag; + result.collections.push(collection); + } + + result.collectionRelationships.push([result.ciphers.length, collectionIndex]); + }); + } else if (!this.organization && tags != null && tags.length > 0) { + this.processFolder(result, tags[0]); + } + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Notes, ""); + cipher.notes += "\n\n" + this.getValueOrDefault(value["Shared Notes"], "") + "\n"; + cipher.name = this.getValueOrDefault(value["Entry Name"], "--"); + cipher.login.username = this.getValueOrDefault(value["User ID"]); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + + if (value.__parsed_extra != null && value.__parsed_extra.length > 0) { + value.__parsed_extra.forEach((extra: string) => { + if (!this.isNullOrWhitespace(extra)) { + cipher.notes += "\n" + extra; + } + }); + } + + const fieldsJson = !this.isNullOrWhitespace(value["Extra Fields"]) + ? JSON.parse(value["Extra Fields"]) + : null; + const fields = + fieldsJson != null && fieldsJson.extraFields != null && fieldsJson.extraFields.length > 0 + ? fieldsJson.extraFields.map((fieldJson: string) => { + try { + return JSON.parse(fieldJson); + } catch { + // Ignore error + } + return null; + }) + : null; + if (fields != null) { + fields.forEach((f: any) => { + if (f != null) { + this.processKvp(cipher, f.name, f.data); + } + }); + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordAgentCsvImporter.ts b/jslib/common/src/importers/passwordAgentCsvImporter.ts new file mode 100644 index 00000000..cc37f5dd --- /dev/null +++ b/jslib/common/src/importers/passwordAgentCsvImporter.ts @@ -0,0 +1,52 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordAgentCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let newVersion = true; + results.forEach((value) => { + if (value.length !== 5 && value.length < 9) { + return; + } + const altFormat = value.length === 10 && value[0] === "0"; + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[altFormat ? 1 : 0], "--"); + cipher.login.username = this.getValueOrDefault(value[altFormat ? 2 : 1]); + cipher.login.password = this.getValueOrDefault(value[altFormat ? 3 : 2]); + if (value.length === 5) { + newVersion = false; + cipher.notes = this.getValueOrDefault(value[4]); + cipher.login.uris = this.makeUriArray(value[3]); + } else { + const folder = this.getValueOrDefault(value[altFormat ? 9 : 8], "(None)"); + let folderName = folder !== "(None)" ? folder.split("\\").join("/") : null; + if (folderName != null) { + folderName = folder.split(" > ").join("/"); + folderName = folder.split(">").join("/"); + } + this.processFolder(result, folderName); + cipher.notes = this.getValueOrDefault(value[altFormat ? 5 : 3]); + cipher.login.uris = this.makeUriArray(value[4]); + } + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (newVersion && this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordBossJsonImporter.ts b/jslib/common/src/importers/passwordBossJsonImporter.ts new file mode 100644 index 00000000..d60a84fb --- /dev/null +++ b/jslib/common/src/importers/passwordBossJsonImporter.ts @@ -0,0 +1,130 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { FolderView } from "../models/view/folderView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordBossJsonImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = JSON.parse(data); + if (results == null || results.items == null) { + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + results.folders.forEach((value: any) => { + foldersMap.set(value.id, value.name); + }); + const foldersIndexMap = new Map(); + foldersMap.forEach((val, key) => { + foldersIndexMap.set(key, result.folders.length); + const f = new FolderView(); + f.name = val; + result.folders.push(f); + }); + + results.items.forEach((value: any) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.login.uris = this.makeUriArray(value.login_url); + + if (value.folder != null && foldersIndexMap.has(value.folder)) { + result.folderRelationships.push([result.ciphers.length, foldersIndexMap.get(value.folder)]); + } + + if (value.identifiers == null) { + return; + } + + if (!this.isNullOrWhitespace(value.identifiers.notes)) { + cipher.notes = value.identifiers.notes.split("\\r\\n").join("\n").split("\\n").join("\n"); + } + + if (value.type === "CreditCard") { + cipher.card = new CardView(); + cipher.type = CipherType.Card; + } + + for (const property in value.identifiers) { + // eslint-disable-next-line + if (!value.identifiers.hasOwnProperty(property)) { + continue; + } + const valObj = value.identifiers[property]; + const val = valObj != null ? valObj.toString() : null; + if ( + this.isNullOrWhitespace(val) || + property === "notes" || + property === "ignoreItemInSecurityScore" + ) { + continue; + } + + if (property === "custom_fields") { + valObj.forEach((cf: any) => { + this.processKvp(cipher, cf.name, cf.value); + }); + continue; + } + + if (cipher.type === CipherType.Card) { + if (property === "cardNumber") { + cipher.card.number = val; + cipher.card.brand = this.getCardBrand(val); + continue; + } else if (property === "nameOnCard") { + cipher.card.cardholderName = val; + continue; + } else if (property === "security_code") { + cipher.card.code = val; + continue; + } else if (property === "expires") { + try { + const expDate = new Date(val); + cipher.card.expYear = expDate.getFullYear().toString(); + cipher.card.expMonth = (expDate.getMonth() + 1).toString(); + } catch { + // Ignore error + } + continue; + } else if (property === "cardType") { + continue; + } + } else { + if ( + (property === "username" || property === "email") && + this.isNullOrWhitespace(cipher.login.username) + ) { + cipher.login.username = val; + continue; + } else if (property === "password") { + cipher.login.password = val; + continue; + } else if (property === "totp") { + cipher.login.totp = val; + continue; + } else if ( + (cipher.login.uris == null || cipher.login.uris.length === 0) && + this.uriFieldNames.indexOf(property) > -1 + ) { + cipher.login.uris = this.makeUriArray(val); + continue; + } + } + + this.processKvp(cipher, property, val); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordDragonXmlImporter.ts b/jslib/common/src/importers/passwordDragonXmlImporter.ts new file mode 100644 index 00000000..433950ad --- /dev/null +++ b/jslib/common/src/importers/passwordDragonXmlImporter.ts @@ -0,0 +1,63 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordDragonXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const records = doc.querySelectorAll("PasswordManager > record"); + Array.from(records).forEach((record) => { + const category = this.querySelectorDirectChild(record, "Category"); + const categoryText = + category != null && + !this.isNullOrWhitespace(category.textContent) && + category.textContent !== "Unfiled" + ? category.textContent + : null; + this.processFolder(result, categoryText); + + const accountName = this.querySelectorDirectChild(record, "Account-Name"); + const userId = this.querySelectorDirectChild(record, "User-Id"); + const password = this.querySelectorDirectChild(record, "Password"); + const url = this.querySelectorDirectChild(record, "URL"); + const notes = this.querySelectorDirectChild(record, "Notes"); + const cipher = this.initLoginCipher(); + cipher.name = + accountName != null ? this.getValueOrDefault(accountName.textContent, "--") : "--"; + cipher.notes = notes != null ? this.getValueOrDefault(notes.textContent) : ""; + cipher.login.username = userId != null ? this.getValueOrDefault(userId.textContent) : null; + cipher.login.password = + password != null ? this.getValueOrDefault(password.textContent) : null; + cipher.login.uris = url != null ? this.makeUriArray(url.textContent) : null; + + const attributes: string[] = []; + for (let i = 1; i <= 10; i++) { + attributes.push("Attribute-" + i); + } + + this.querySelectorAllDirectChild(record, attributes.join(",")).forEach((attr) => { + if (this.isNullOrWhitespace(attr.textContent) || attr.textContent === "null") { + return; + } + this.processKvp(cipher, attr.tagName, attr.textContent); + }); + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordSafeXmlImporter.ts b/jslib/common/src/importers/passwordSafeXmlImporter.ts new file mode 100644 index 00000000..b7aa9358 --- /dev/null +++ b/jslib/common/src/importers/passwordSafeXmlImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordSafeXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const passwordSafe = doc.querySelector("passwordsafe"); + if (passwordSafe == null) { + result.errorMessage = "Missing `passwordsafe` node."; + result.success = false; + return Promise.resolve(result); + } + + const notesDelimiter = passwordSafe.getAttribute("delimiter"); + const entries = doc.querySelectorAll("passwordsafe > entry"); + Array.from(entries).forEach((entry) => { + const group = this.querySelectorDirectChild(entry, "group"); + const groupText = + group != null && !this.isNullOrWhitespace(group.textContent) + ? group.textContent.split(".").join("/") + : null; + this.processFolder(result, groupText); + + const title = this.querySelectorDirectChild(entry, "title"); + const username = this.querySelectorDirectChild(entry, "username"); + const email = this.querySelectorDirectChild(entry, "email"); + const password = this.querySelectorDirectChild(entry, "password"); + const url = this.querySelectorDirectChild(entry, "url"); + const notes = this.querySelectorDirectChild(entry, "notes"); + const cipher = this.initLoginCipher(); + cipher.name = title != null ? this.getValueOrDefault(title.textContent, "--") : "--"; + cipher.notes = + notes != null + ? this.getValueOrDefault(notes.textContent, "").split(notesDelimiter).join("\n") + : null; + cipher.login.username = + username != null ? this.getValueOrDefault(username.textContent) : null; + cipher.login.password = + password != null ? this.getValueOrDefault(password.textContent) : null; + cipher.login.uris = url != null ? this.makeUriArray(url.textContent) : null; + + if (this.isNullOrWhitespace(cipher.login.username) && email != null) { + cipher.login.username = this.getValueOrDefault(email.textContent); + } else if (email != null && !this.isNullOrWhitespace(email.textContent)) { + cipher.notes = this.isNullOrWhitespace(cipher.notes) + ? "Email: " + email.textContent + : cipher.notes + "\n" + "Email: " + email.textContent; + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/passwordWalletTxtImporter.ts b/jslib/common/src/importers/passwordWalletTxtImporter.ts new file mode 100644 index 00000000..b5940372 --- /dev/null +++ b/jslib/common/src/importers/passwordWalletTxtImporter.ts @@ -0,0 +1,47 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class PasswordWalletTxtImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 1) { + return; + } + if (value.length > 5) { + this.processFolder(result, value[5]); + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + if (value.length > 4) { + cipher.notes = this.getValueOrDefault(value[4], "").split("¬").join("\n"); + } + if (value.length > 2) { + cipher.login.username = this.getValueOrDefault(value[2]); + } + if (value.length > 3) { + cipher.login.password = this.getValueOrDefault(value[3]); + } + if (value.length > 1) { + cipher.login.uris = this.makeUriArray(value[1]); + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/rememBearCsvImporter.ts b/jslib/common/src/importers/rememBearCsvImporter.ts new file mode 100644 index 00000000..3f7dc464 --- /dev/null +++ b/jslib/common/src/importers/rememBearCsvImporter.ts @@ -0,0 +1,75 @@ +import { CipherType } from "../enums/cipherType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class RememBearCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.trash === "true") { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.name); + cipher.notes = this.getValueOrDefault(value.notes); + if (value.type === "LoginItem") { + cipher.login.uris = this.makeUriArray(value.website); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.username = this.getValueOrDefault(value.username); + } else if (value.type === "CreditCardItem") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.cardholder); + cipher.card.number = this.getValueOrDefault(value.number); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(value.verification); + + try { + const expMonth = this.getValueOrDefault(value.expiryMonth); + if (expMonth != null) { + const expMonthNumber = parseInt(expMonth, null); + if (expMonthNumber != null && expMonthNumber >= 1 && expMonthNumber <= 12) { + cipher.card.expMonth = expMonthNumber.toString(); + } + } + } catch { + // Ignore error + } + try { + const expYear = this.getValueOrDefault(value.expiryYear); + if (expYear != null) { + const expYearNumber = parseInt(expYear, null); + if (expYearNumber != null) { + cipher.card.expYear = expYearNumber.toString(); + } + } + } catch { + // Ignore error + } + + const pin = this.getValueOrDefault(value.pin); + if (pin != null) { + this.processKvp(cipher, "PIN", pin); + } + const zip = this.getValueOrDefault(value.zipCode); + if (zip != null) { + this.processKvp(cipher, "Zip Code", zip); + } + } + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/roboformCsvImporter.ts b/jslib/common/src/importers/roboformCsvImporter.ts new file mode 100644 index 00000000..c5279e2b --- /dev/null +++ b/jslib/common/src/importers/roboformCsvImporter.ts @@ -0,0 +1,69 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class RoboFormCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + let i = 1; + results.forEach((value) => { + const folder = + !this.isNullOrWhitespace(value.Folder) && value.Folder.startsWith("/") + ? value.Folder.replace("/", "") + : value.Folder; + const folderName = !this.isNullOrWhitespace(folder) ? folder : null; + this.processFolder(result, folderName); + + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value.Note); + cipher.name = this.getValueOrDefault(value.Name, "--"); + cipher.login.username = this.getValueOrDefault(value.Login); + cipher.login.password = this.getValueOrDefault(value.Pwd); + cipher.login.uris = this.makeUriArray(value.Url); + + if (!this.isNullOrWhitespace(value.Rf_fields)) { + let fields: string[] = [value.Rf_fields]; + if (value.__parsed_extra != null && value.__parsed_extra.length > 0) { + fields = fields.concat(value.__parsed_extra); + } + fields.forEach((field: string) => { + const parts = field.split(":"); + if (parts.length < 3) { + return; + } + const key = parts[0] === "-no-name-" ? null : parts[0]; + const val = parts.length === 4 && parts[2] === "rck" ? parts[1] : parts[2]; + this.processKvp(cipher, key, val); + }); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + + if ( + i === results.length && + cipher.name === "--" && + this.isNullOrWhitespace(cipher.login.password) + ) { + return; + } + + result.ciphers.push(cipher); + i++; + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/safariCsvImporter.ts b/jslib/common/src/importers/safariCsvImporter.ts new file mode 100644 index 00000000..786b2827 --- /dev/null +++ b/jslib/common/src/importers/safariCsvImporter.ts @@ -0,0 +1,30 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SafariCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Title, "--"); + cipher.login.username = this.getValueOrDefault(value.Username); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.Url ?? value.URL); + cipher.login.totp = this.getValueOrDefault(value.OTPAuth); + cipher.notes = this.getValueOrDefault(value.Notes); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/safeInCloudXmlImporter.ts b/jslib/common/src/importers/safeInCloudXmlImporter.ts new file mode 100644 index 00000000..3e5a8199 --- /dev/null +++ b/jslib/common/src/importers/safeInCloudXmlImporter.ts @@ -0,0 +1,132 @@ +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; +import { FolderView } from "../models/view/folderView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SafeInCloudXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const db = doc.querySelector("database"); + if (db == null) { + result.errorMessage = "Missing `database` node."; + result.success = false; + return Promise.resolve(result); + } + + const foldersMap = new Map(); + + Array.from(doc.querySelectorAll("database > label")).forEach((labelEl) => { + const name = labelEl.getAttribute("name"); + const id = labelEl.getAttribute("id"); + if (!this.isNullOrWhitespace(name) && !this.isNullOrWhitespace(id)) { + foldersMap.set(id, result.folders.length); + const folder = new FolderView(); + folder.name = name; + result.folders.push(folder); + } + }); + + Array.from(doc.querySelectorAll("database > card")).forEach((cardEl) => { + if (cardEl.getAttribute("template") === "true" || cardEl.getAttribute("deleted") === "true") { + return; + } + + const labelIdEl = this.querySelectorDirectChild(cardEl, "label_id"); + if (labelIdEl != null) { + const labelId = labelIdEl.textContent; + if (!this.isNullOrWhitespace(labelId) && foldersMap.has(labelId)) { + result.folderRelationships.push([result.ciphers.length, foldersMap.get(labelId)]); + } + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(cardEl.getAttribute("title"), "--"); + + if (cardEl.getAttribute("star") === "true") { + cipher.favorite = true; + } + + const cardType = cardEl.getAttribute("type"); + if (cardType === "note") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + } else { + Array.from(this.querySelectorAllDirectChild(cardEl, "field")).forEach((fieldEl) => { + const text = fieldEl.textContent; + if (this.isNullOrWhitespace(text)) { + return; + } + const name = fieldEl.getAttribute("name"); + const fieldType = this.getValueOrDefault(fieldEl.getAttribute("type"), "").toLowerCase(); + if (fieldType === "login") { + cipher.login.username = text; + } else if (fieldType === "password" || fieldType === "secret") { + // safeInCloud allows for more than one password. we just insert them here and find the one used as password later + this.processKvp(cipher, name, text, FieldType.Hidden); + } else if (fieldType === "one_time_password") { + cipher.login.totp = text; + } else if (fieldType === "notes") { + cipher.notes += text + "\n"; + } else if (fieldType === "weblogin" || fieldType === "website") { + cipher.login.uris = this.makeUriArray(text); + } else { + this.processKvp(cipher, name, text); + } + }); + } + + Array.from(this.querySelectorAllDirectChild(cardEl, "notes")).forEach((notesEl) => { + cipher.notes += notesEl.textContent + "\n"; + }); + + this.setPassword(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + // Choose a password from all passwords. Take one that has password in its name, or the first one if there is no such entry + // if its name is password, we can safely remove it form the fields. otherwise, it would maybe be best to keep it as a hidden field + setPassword(cipher: CipherView) { + const candidates = cipher.fields.filter((field) => field.type === FieldType.Hidden); + if (!candidates.length) { + return; + } + + let choice: FieldView; + for (const field of candidates) { + if (this.passwordFieldNames.includes(field.name.toLowerCase())) { + choice = field; + cipher.fields = cipher.fields.filter((f) => f !== choice); + break; + } + } + + if (!choice) { + choice = candidates[0]; + } + + cipher.login.password = choice.value; + } +} diff --git a/jslib/common/src/importers/saferpassCsvImport.ts b/jslib/common/src/importers/saferpassCsvImport.ts new file mode 100644 index 00000000..6d962ba1 --- /dev/null +++ b/jslib/common/src/importers/saferpassCsvImport.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SaferPassCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(this.nameFromUrl(value.url), "--"); + cipher.notes = this.getValueOrDefault(value.notes); + cipher.login.username = this.getValueOrDefault(value.username); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/secureSafeCsvImporter.ts b/jslib/common/src/importers/secureSafeCsvImporter.ts new file mode 100644 index 00000000..8d0ac1fe --- /dev/null +++ b/jslib/common/src/importers/secureSafeCsvImporter.ts @@ -0,0 +1,29 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SecureSafeCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Title); + cipher.notes = this.getValueOrDefault(value.Comment); + cipher.login.uris = this.makeUriArray(value.Url); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.username = this.getValueOrDefault(value.Username); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/splashIdCsvImporter.ts b/jslib/common/src/importers/splashIdCsvImporter.ts new file mode 100644 index 00000000..df1118d1 --- /dev/null +++ b/jslib/common/src/importers/splashIdCsvImporter.ts @@ -0,0 +1,57 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class SplashIdCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length < 3) { + return; + } + + this.processFolder(result, this.getValueOrDefault(value[value.length - 1])); + const cipher = this.initLoginCipher(); + cipher.notes = this.getValueOrDefault(value[value.length - 2], ""); + cipher.name = this.getValueOrDefault(value[1], "--"); + + if (value[0] === "Web Logins" || value[0] === "Servers" || value[0] === "Email Accounts") { + cipher.login.username = this.getValueOrDefault(value[2]); + cipher.login.password = this.getValueOrDefault(value[3]); + cipher.login.uris = this.makeUriArray(value[4]); + this.parseFieldsToNotes(cipher, 5, value); + } else { + this.parseFieldsToNotes(cipher, 2, value); + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseFieldsToNotes(cipher: CipherView, startIndex: number, value: any) { + // last 3 rows do not get parsed + for (let i = startIndex; i < value.length - 3; i++) { + if (this.isNullOrWhitespace(value[i])) { + continue; + } + cipher.notes += value[i] + "\n"; + } + } +} diff --git a/jslib/common/src/importers/stickyPasswordXmlImporter.ts b/jslib/common/src/importers/stickyPasswordXmlImporter.ts new file mode 100644 index 00000000..caaa3370 --- /dev/null +++ b/jslib/common/src/importers/stickyPasswordXmlImporter.ts @@ -0,0 +1,83 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class StickyPasswordXmlImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const doc = this.parseXml(data); + if (doc == null) { + result.success = false; + return Promise.resolve(result); + } + + const loginNodes = doc.querySelectorAll("root > Database > Logins > Login"); + Array.from(loginNodes).forEach((loginNode) => { + const accountId = loginNode.getAttribute("ID"); + if (this.isNullOrWhitespace(accountId)) { + return; + } + + const usernameText = loginNode.getAttribute("Name"); + const passwordText = loginNode.getAttribute("Password"); + let titleText: string = null; + let linkText: string = null; + let notesText: string = null; + let groupId: string = null; + let groupText: string = null; + + const accountLogin = doc.querySelector( + "root > Database > Accounts > Account > " + + 'LoginLinks > Login[SourceLoginID="' + + accountId + + '"]' + ); + if (accountLogin != null) { + const account = accountLogin.parentElement.parentElement; + if (account != null) { + titleText = account.getAttribute("Name"); + linkText = account.getAttribute("Link"); + groupId = account.getAttribute("ParentID"); + notesText = account.getAttribute("Comments"); + if (!this.isNullOrWhitespace(notesText)) { + notesText = notesText.split("/n").join("\n"); + } + } + } + + if (!this.isNullOrWhitespace(groupId)) { + groupText = this.buildGroupText(doc, groupId, ""); + this.processFolder(result, groupText); + } + + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(titleText, "--"); + cipher.notes = this.getValueOrDefault(notesText); + cipher.login.username = this.getValueOrDefault(usernameText); + cipher.login.password = this.getValueOrDefault(passwordText); + cipher.login.uris = this.makeUriArray(linkText); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + buildGroupText(doc: Document, groupId: string, groupText: string): string { + const group = doc.querySelector('root > Database > Groups > Group[ID="' + groupId + '"]'); + if (group == null) { + return groupText; + } + if (!this.isNullOrWhitespace(groupText)) { + groupText = "/" + groupText; + } + groupText = group.getAttribute("Name") + groupText; + return this.buildGroupText(doc, group.getAttribute("ParentID"), groupText); + } +} diff --git a/jslib/common/src/importers/truekeyCsvImporter.ts b/jslib/common/src/importers/truekeyCsvImporter.ts new file mode 100644 index 00000000..d28af7af --- /dev/null +++ b/jslib/common/src/importers/truekeyCsvImporter.ts @@ -0,0 +1,87 @@ +import { CipherType } from "../enums/cipherType"; +import { SecureNoteType } from "../enums/secureNoteType"; +import { ImportResult } from "../models/domain/importResult"; +import { CardView } from "../models/view/cardView"; +import { SecureNoteView } from "../models/view/secureNoteView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +const PropertiesToIgnore = [ + "kind", + "autologin", + "favorite", + "hexcolor", + "protectedwithpassword", + "subdomainonly", + "type", + "tk_export_version", + "note", + "title", + "document_content", +]; + +export class TrueKeyCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.favorite, "").toLowerCase() === "true"; + cipher.name = this.getValueOrDefault(value.name, "--"); + cipher.notes = this.getValueOrDefault(value.memo, ""); + cipher.login.username = this.getValueOrDefault(value.login); + cipher.login.password = this.getValueOrDefault(value.password); + cipher.login.uris = this.makeUriArray(value.url); + + if (value.kind !== "login") { + cipher.name = this.getValueOrDefault(value.title, "--"); + cipher.notes = this.getValueOrDefault(value.note, ""); + } + + if (value.kind === "cc") { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(value.cardholder); + cipher.card.number = this.getValueOrDefault(value.number); + cipher.card.brand = this.getCardBrand(cipher.card.number); + if (!this.isNullOrWhitespace(value.expiryDate)) { + try { + const expDate = new Date(value.expiryDate); + cipher.card.expYear = expDate.getFullYear().toString(); + cipher.card.expMonth = (expDate.getMonth() + 1).toString(); + } catch { + // Ignore error + } + } + } else if (value.kind !== "login") { + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + if (!this.isNullOrWhitespace(cipher.notes)) { + cipher.notes = this.getValueOrDefault(value.document_content, ""); + } + for (const property in value) { + if ( + value.hasOwnProperty(property) && // eslint-disable-line + PropertiesToIgnore.indexOf(property.toLowerCase()) < 0 && + !this.isNullOrWhitespace(value[property]) + ) { + this.processKvp(cipher, property, value[property]); + } + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/upmCsvImporter.ts b/jslib/common/src/importers/upmCsvImporter.ts new file mode 100644 index 00000000..ea092ffc --- /dev/null +++ b/jslib/common/src/importers/upmCsvImporter.ts @@ -0,0 +1,32 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class UpmCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, false); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if (value.length !== 5) { + return; + } + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value[0], "--"); + cipher.notes = this.getValueOrDefault(value[4]); + cipher.login.username = this.getValueOrDefault(value[1]); + cipher.login.password = this.getValueOrDefault(value[2]); + cipher.login.uris = this.makeUriArray(value[3]); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/yotiCsvImporter.ts b/jslib/common/src/importers/yotiCsvImporter.ts new file mode 100644 index 00000000..526f7d69 --- /dev/null +++ b/jslib/common/src/importers/yotiCsvImporter.ts @@ -0,0 +1,28 @@ +import { ImportResult } from "../models/domain/importResult"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class YotiCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(value.Name, "--"); + cipher.login.username = this.getValueOrDefault(value["User name"]); + cipher.login.password = this.getValueOrDefault(value.Password); + cipher.login.uris = this.makeUriArray(value.URL); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + result.success = true; + return Promise.resolve(result); + } +} diff --git a/jslib/common/src/importers/zohoVaultCsvImporter.ts b/jslib/common/src/importers/zohoVaultCsvImporter.ts new file mode 100644 index 00000000..18008e80 --- /dev/null +++ b/jslib/common/src/importers/zohoVaultCsvImporter.ts @@ -0,0 +1,81 @@ +import { ImportResult } from "../models/domain/importResult"; +import { CipherView } from "../models/view/cipherView"; + +import { BaseImporter } from "./baseImporter"; +import { Importer } from "./importer"; + +export class ZohoVaultCsvImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results = this.parseCsv(data, true); + if (results == null) { + result.success = false; + return Promise.resolve(result); + } + + results.forEach((value) => { + if ( + this.isNullOrWhitespace(value["Password Name"]) && + this.isNullOrWhitespace(value["Secret Name"]) + ) { + return; + } + this.processFolder(result, this.getValueOrDefault(value.ChamberName)); + const cipher = this.initLoginCipher(); + cipher.favorite = this.getValueOrDefault(value.Favorite, "0") === "1"; + cipher.notes = this.getValueOrDefault(value.Notes); + cipher.name = this.getValueOrDefault( + value["Password Name"], + this.getValueOrDefault(value["Secret Name"], "--") + ); + cipher.login.uris = this.makeUriArray( + this.getValueOrDefault(value["Password URL"], this.getValueOrDefault(value["Secret URL"])) + ); + this.parseData(cipher, value.SecretData); + this.parseData(cipher, value.CustomData); + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + }); + + if (this.organization) { + this.moveFoldersToCollections(result); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseData(cipher: CipherView, data: string) { + if (this.isNullOrWhitespace(data)) { + return; + } + const dataLines = this.splitNewLine(data); + dataLines.forEach((line) => { + const delimPosition = line.indexOf(":"); + if (delimPosition < 0) { + return; + } + const field = line.substring(0, delimPosition); + const value = line.length > delimPosition ? line.substring(delimPosition + 1) : null; + if ( + this.isNullOrWhitespace(field) || + this.isNullOrWhitespace(value) || + field === "SecretType" + ) { + return; + } + const fieldLower = field.toLowerCase(); + if (cipher.login.username == null && this.usernameFieldNames.indexOf(fieldLower) > -1) { + cipher.login.username = value; + } else if ( + cipher.login.password == null && + this.passwordFieldNames.indexOf(fieldLower) > -1 + ) { + cipher.login.password = value; + } else { + this.processKvp(cipher, field, value); + } + }); + } +} diff --git a/jslib/common/src/misc/captcha_iframe.ts b/jslib/common/src/misc/captcha_iframe.ts new file mode 100644 index 00000000..ca422fc0 --- /dev/null +++ b/jslib/common/src/misc/captcha_iframe.ts @@ -0,0 +1,38 @@ +import { I18nService } from "../abstractions/i18n.service"; + +import { IFrameComponent } from "./iframe_component"; + +export class CaptchaIFrame extends IFrameComponent { + constructor( + win: Window, + webVaultUrl: string, + private i18nService: I18nService, + successCallback: (message: string) => any, + errorCallback: (message: string) => any, + infoCallback: (message: string) => any + ) { + super( + win, + webVaultUrl, + "captcha-connector.html", + "hcaptcha_iframe", + successCallback, + errorCallback, + (message: string) => { + const parsedMessage = JSON.parse(message); + if (typeof parsedMessage !== "string") { + this.iframe.height = parsedMessage.height.toString(); + this.iframe.width = parsedMessage.width.toString(); + } else { + infoCallback(parsedMessage); + } + } + ); + } + + init(siteKey: string): void { + super.initComponent( + this.createParams({ siteKey: siteKey, locale: this.i18nService.translationLocale }, 1) + ); + } +} diff --git a/jslib/common/src/misc/iframe_component.ts b/jslib/common/src/misc/iframe_component.ts new file mode 100644 index 00000000..2cd702ad --- /dev/null +++ b/jslib/common/src/misc/iframe_component.ts @@ -0,0 +1,94 @@ +export abstract class IFrameComponent { + iframe: HTMLIFrameElement; + private connectorLink: HTMLAnchorElement; + private parseFunction = this.parseMessage.bind(this); + + constructor( + private win: Window, + protected webVaultUrl: string, + private path: string, + private iframeId: string, + public successCallback?: (message: string) => any, + public errorCallback?: (message: string) => any, + public infoCallback?: (message: string) => any + ) { + this.connectorLink = win.document.createElement("a"); + } + + stop() { + this.sendMessage("stop"); + } + + start() { + this.sendMessage("start"); + } + + sendMessage(message: any) { + if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) { + return; + } + + this.iframe.contentWindow.postMessage(message, this.iframe.src); + } + + base64Encode(str: string): string { + return btoa( + encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => { + return String.fromCharCode(("0x" + p1) as any); + }) + ); + } + + cleanup() { + this.win.removeEventListener("message", this.parseFunction, false); + } + + protected createParams(data: any, version: number) { + return new URLSearchParams({ + data: this.base64Encode(JSON.stringify(data)), + parent: encodeURIComponent(this.win.document.location.href), + v: version.toString(), + }); + } + + protected initComponent(params: URLSearchParams): void { + this.connectorLink.href = `${this.webVaultUrl}/${this.path}?${params}`; + this.iframe = this.win.document.getElementById(this.iframeId) as HTMLIFrameElement; + this.iframe.src = this.connectorLink.href; + + this.win.addEventListener("message", this.parseFunction, false); + } + + private parseMessage(event: MessageEvent) { + if (!this.validMessage(event)) { + return; + } + + const parts: string[] = event.data.split("|"); + if (parts[0] === "success" && this.successCallback) { + this.successCallback(parts[1]); + } else if (parts[0] === "error" && this.errorCallback) { + this.errorCallback(parts[1]); + } else if (parts[0] === "info" && this.infoCallback) { + this.infoCallback(parts[1]); + } + } + + private validMessage(event: MessageEvent) { + if ( + event.origin == null || + event.origin === "" || + event.origin !== (this.connectorLink as any).origin || + event.data == null || + typeof event.data !== "string" + ) { + return false; + } + + return ( + event.data.indexOf("success|") === 0 || + event.data.indexOf("error|") === 0 || + event.data.indexOf("info|") === 0 + ); + } +} diff --git a/jslib/common/src/misc/linkedFieldOption.decorator.ts b/jslib/common/src/misc/linkedFieldOption.decorator.ts new file mode 100644 index 00000000..43836318 --- /dev/null +++ b/jslib/common/src/misc/linkedFieldOption.decorator.ts @@ -0,0 +1,27 @@ +import { LinkedIdType } from "../enums/linkedIdType"; +import { ItemView } from "../models/view/itemView"; + +export class LinkedMetadata { + constructor(readonly propertyKey: string, private readonly _i18nKey?: string) {} + + get i18nKey() { + return this._i18nKey ?? this.propertyKey; + } +} + +/** + * A decorator used to set metadata used by Linked custom fields. Apply it to a class property or getter to make it + * available as a Linked custom field option. + * @param id - A unique value that is saved in the Field model. It is used to look up the decorated class property. + * @param i18nKey - The i18n key used to describe the decorated class property in the UI. If it is null, then the name + * of the class property will be used as the i18n key. + */ +export function linkedFieldOption(id: LinkedIdType, i18nKey?: string) { + return (prototype: ItemView, propertyKey: string) => { + if (prototype.linkedFieldOptions == null) { + prototype.linkedFieldOptions = new Map(); + } + + prototype.linkedFieldOptions.set(id, new LinkedMetadata(propertyKey, i18nKey)); + }; +} diff --git a/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts new file mode 100644 index 00000000..89ebc379 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/apiLogin.strategy.ts @@ -0,0 +1,70 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { EnvironmentService } from "../../abstractions/environment.service"; +import { KeyConnectorService } from "../../abstractions/keyConnector.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { ApiLogInCredentials } from "../../models/domain/logInCredentials"; +import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class ApiLogInStrategy extends LogInStrategy { + tokenRequest: ApiTokenRequest; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private environmentService: EnvironmentService, + private keyConnectorService: KeyConnectorService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) { + if (tokenResponse.apiUseKeyConnector) { + const keyConnectorUrl = this.environmentService.getKeyConnectorUrl(); + await this.keyConnectorService.getAndSetKey(keyConnectorUrl); + } + } + + async logIn(credentials: ApiLogInCredentials) { + this.tokenRequest = new ApiTokenRequest( + credentials.clientId, + credentials.clientSecret, + await this.buildTwoFactor(), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } + + protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) { + await super.saveAccountInformation(tokenResponse); + await this.stateService.setApiKeyClientId(this.tokenRequest.clientId); + await this.stateService.setApiKeyClientSecret(this.tokenRequest.clientSecret); + } +} diff --git a/jslib/common/src/misc/logInStrategies/logIn.strategy.ts b/jslib/common/src/misc/logInStrategies/logIn.strategy.ts new file mode 100644 index 00000000..a58a7bc8 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/logIn.strategy.ts @@ -0,0 +1,170 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; +import { Account, AccountProfile, AccountTokens } from "../../models/domain/account"; +import { AuthResult } from "../../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../../models/domain/logInCredentials"; +import { DeviceRequest } from "../../models/request/deviceRequest"; +import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest"; +import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequestTwoFactor"; +import { KeysRequest } from "../../models/request/keysRequest"; +import { IdentityCaptchaResponse } from "../../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../../models/response/identityTwoFactorResponse"; + +export abstract class LogInStrategy { + protected abstract tokenRequest: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest; + protected captchaBypassToken: string = null; + + constructor( + protected cryptoService: CryptoService, + protected apiService: ApiService, + protected tokenService: TokenService, + protected appIdService: AppIdService, + protected platformUtilsService: PlatformUtilsService, + protected messagingService: MessagingService, + protected logService: LogService, + protected stateService: StateService, + protected twoFactorService: TwoFactorService + ) {} + + abstract logIn( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ): Promise; + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string = null + ): Promise { + this.tokenRequest.setTwoFactor(twoFactor); + return this.startLogIn(); + } + + protected async startLogIn(): Promise { + this.twoFactorService.clearSelectedProvider(); + + const response = await this.apiService.postIdentityToken(this.tokenRequest); + + if (response instanceof IdentityTwoFactorResponse) { + return this.processTwoFactorResponse(response); + } else if (response instanceof IdentityCaptchaResponse) { + return this.processCaptchaResponse(response); + } else if (response instanceof IdentityTokenResponse) { + return this.processTokenResponse(response); + } + + throw new Error("Invalid response object."); + } + + protected onSuccessfulLogin(response: IdentityTokenResponse): Promise { + // Implemented in subclass if required + return null; + } + + protected async buildDeviceRequest() { + const appId = await this.appIdService.getAppId(); + return new DeviceRequest(appId, this.platformUtilsService); + } + + protected async buildTwoFactor(userProvidedTwoFactor?: TokenRequestTwoFactor) { + if (userProvidedTwoFactor != null) { + return userProvidedTwoFactor; + } + + const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(); + if (storedTwoFactorToken != null) { + return new TokenRequestTwoFactor(TwoFactorProviderType.Remember, storedTwoFactorToken, false); + } + + return new TokenRequestTwoFactor(); + } + + protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) { + const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken); + await this.stateService.addAccount( + new Account({ + profile: { + ...new AccountProfile(), + ...{ + userId: accountInformation.sub, + email: accountInformation.email, + hasPremiumPersonally: accountInformation.premium, + kdfIterations: tokenResponse.kdfIterations, + kdfType: tokenResponse.kdf, + }, + }, + tokens: { + ...new AccountTokens(), + ...{ + accessToken: tokenResponse.accessToken, + refreshToken: tokenResponse.refreshToken, + }, + }, + }) + ); + } + + protected async processTokenResponse(response: IdentityTokenResponse): Promise { + const result = new AuthResult(); + result.resetMasterPassword = response.resetMasterPassword; + result.forcePasswordReset = response.forcePasswordReset; + + await this.saveAccountInformation(response); + + if (response.twoFactorToken != null) { + await this.tokenService.setTwoFactorToken(response); + } + + const newSsoUser = response.key == null; + if (!newSsoUser) { + await this.cryptoService.setEncKey(response.key); + await this.cryptoService.setEncPrivateKey( + response.privateKey ?? (await this.createKeyPairForOldAccount()) + ); + } + + await this.onSuccessfulLogin(response); + + await this.stateService.setBiometricLocked(false); + this.messagingService.send("loggedIn"); + + return result; + } + + private async processTwoFactorResponse(response: IdentityTwoFactorResponse): Promise { + const result = new AuthResult(); + result.twoFactorProviders = response.twoFactorProviders2; + this.twoFactorService.setProviders(response); + this.captchaBypassToken = response.captchaToken ?? null; + return result; + } + + private async processCaptchaResponse(response: IdentityCaptchaResponse): Promise { + const result = new AuthResult(); + result.captchaSiteKey = response.siteKey; + return result; + } + + private async createKeyPairForOldAccount() { + try { + const [publicKey, privateKey] = await this.cryptoService.makeKeyPair(); + await this.apiService.postAccountKeys(new KeysRequest(publicKey, privateKey.encryptedString)); + return privateKey.encryptedString; + } catch (e) { + this.logService.error(e); + } + } +} diff --git a/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts new file mode 100644 index 00000000..81947e56 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/passwordLogin.strategy.ts @@ -0,0 +1,95 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { AuthService } from "../../abstractions/auth.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { HashPurpose } from "../../enums/hashPurpose"; +import { AuthResult } from "../../models/domain/authResult"; +import { PasswordLogInCredentials } from "../../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../../models/domain/symmetricCryptoKey"; +import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest"; +import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequestTwoFactor"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class PasswordLogInStrategy extends LogInStrategy { + get email() { + return this.tokenRequest.email; + } + + get masterPasswordHash() { + return this.tokenRequest.masterPasswordHash; + } + + tokenRequest: PasswordTokenRequest; + + private localHashedPassword: string; + private key: SymmetricCryptoKey; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private authService: AuthService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin() { + await this.cryptoService.setKey(this.key); + await this.cryptoService.setKeyHash(this.localHashedPassword); + } + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ): Promise { + this.tokenRequest.captchaResponse = captchaResponse ?? this.captchaBypassToken; + return super.logInTwoFactor(twoFactor); + } + + async logIn(credentials: PasswordLogInCredentials) { + const { email, masterPassword, captchaToken, twoFactor } = credentials; + + this.key = await this.authService.makePreloginKey(masterPassword, email); + + // Hash the password early (before authentication) so we don't persist it in memory in plaintext + this.localHashedPassword = await this.cryptoService.hashPassword( + masterPassword, + this.key, + HashPurpose.LocalAuthorization + ); + const hashedPassword = await this.cryptoService.hashPassword(masterPassword, this.key); + + this.tokenRequest = new PasswordTokenRequest( + email, + hashedPassword, + captchaToken, + await this.buildTwoFactor(twoFactor), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } +} diff --git a/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts b/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts new file mode 100644 index 00000000..e56bd7a3 --- /dev/null +++ b/jslib/common/src/misc/logInStrategies/ssoLogin.strategy.ts @@ -0,0 +1,70 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { KeyConnectorService } from "../../abstractions/keyConnector.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { SsoLogInCredentials } from "../../models/domain/logInCredentials"; +import { SsoTokenRequest } from "../../models/request/identityToken/ssoTokenRequest"; +import { IdentityTokenResponse } from "../../models/response/identityTokenResponse"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class SsoLogInStrategy extends LogInStrategy { + tokenRequest: SsoTokenRequest; + orgId: string; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private keyConnectorService: KeyConnectorService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) { + const newSsoUser = tokenResponse.key == null; + + if (tokenResponse.keyConnectorUrl != null) { + if (!newSsoUser) { + await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl); + } else { + await this.keyConnectorService.convertNewSsoUserToKeyConnector(tokenResponse, this.orgId); + } + } + } + + async logIn(credentials: SsoLogInCredentials) { + this.orgId = credentials.orgId; + this.tokenRequest = new SsoTokenRequest( + credentials.code, + credentials.codeVerifier, + credentials.redirectUrl, + await this.buildTwoFactor(credentials.twoFactor), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } +} diff --git a/jslib/common/src/misc/nodeUtils.ts b/jslib/common/src/misc/nodeUtils.ts new file mode 100644 index 00000000..5b5ba27b --- /dev/null +++ b/jslib/common/src/misc/nodeUtils.ts @@ -0,0 +1,34 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as readline from "readline"; + +export class NodeUtils { + static mkdirpSync(targetDir: string, mode = "700", relative = false, relativeDir: string = null) { + const initialDir = path.isAbsolute(targetDir) ? path.sep : ""; + const baseDir = relative ? (relativeDir != null ? relativeDir : __dirname) : "."; + targetDir.split(path.sep).reduce((parentDir, childDir) => { + const dir = path.resolve(baseDir, parentDir, childDir); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, mode); + } + return dir; + }, initialDir); + } + static readFirstLine(fileName: string) { + return new Promise((resolve, reject) => { + const readStream = fs.createReadStream(fileName, { encoding: "utf8" }); + const readInterface = readline.createInterface(readStream); + readInterface + .on("line", (line) => { + readStream.close(); + resolve(line); + }) + .on("error", (err) => reject(err)); + }); + } + + // https://stackoverflow.com/a/31394257 + static bufferToArrayBuffer(buf: Buffer): ArrayBuffer { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + } +} diff --git a/jslib/common/src/misc/sequentialize.ts b/jslib/common/src/misc/sequentialize.ts new file mode 100644 index 00000000..c2421147 --- /dev/null +++ b/jslib/common/src/misc/sequentialize.ts @@ -0,0 +1,57 @@ +/** + * Use as a Decorator on async functions, it will prevent multiple 'active' calls as the same time + * + * If a promise was returned from a previous call to this function, that hasn't yet resolved it will + * be returned, instead of calling the original function again + * + * Results are not cached, once the promise has returned, the next call will result in a fresh call + * + * Read more at https://github.com/bitwarden/jslib/pull/7 + */ +export function sequentialize(cacheKey: (args: any[]) => string) { + return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { + const originalMethod: () => Promise = descriptor.value; + const caches = new Map>>(); + + const getCache = (obj: any) => { + let cache = caches.get(obj); + if (cache != null) { + return cache; + } + cache = new Map>(); + caches.set(obj, cache); + return cache; + }; + + return { + value: function (...args: any[]) { + const cache = getCache(this); + const argsCacheKey = cacheKey(args); + let response = cache.get(argsCacheKey); + if (response != null) { + return response; + } + + const onFinally = () => { + cache.delete(argsCacheKey); + if (cache.size === 0) { + caches.delete(this); + } + }; + response = originalMethod + .apply(this, args) + .then((val: any) => { + onFinally(); + return val; + }) + .catch((err: any) => { + onFinally(); + throw err; + }); + + cache.set(argsCacheKey, response); + return response; + }, + }; + }; +} diff --git a/jslib/common/src/misc/serviceUtils.ts b/jslib/common/src/misc/serviceUtils.ts new file mode 100644 index 00000000..b6c05092 --- /dev/null +++ b/jslib/common/src/misc/serviceUtils.ts @@ -0,0 +1,72 @@ +import { ITreeNodeObject, TreeNode } from "../models/domain/treeNode"; + +export class ServiceUtils { + static nestedTraverse( + nodeTree: TreeNode[], + partIndex: number, + parts: string[], + obj: ITreeNodeObject, + parent: ITreeNodeObject, + delimiter: string + ) { + if (parts.length <= partIndex) { + return; + } + + const end = partIndex === parts.length - 1; + const partName = parts[partIndex]; + + for (let i = 0; i < nodeTree.length; i++) { + if (nodeTree[i].node.name !== parts[partIndex]) { + continue; + } + if (end && nodeTree[i].node.id !== obj.id) { + // Another node with the same name. + nodeTree.push(new TreeNode(obj, partName, parent)); + return; + } + ServiceUtils.nestedTraverse( + nodeTree[i].children, + partIndex + 1, + parts, + obj, + nodeTree[i].node, + delimiter + ); + return; + } + + if (nodeTree.filter((n) => n.node.name === partName).length === 0) { + if (end) { + nodeTree.push(new TreeNode(obj, partName, parent)); + return; + } + const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1]; + ServiceUtils.nestedTraverse( + nodeTree, + 0, + [newPartName, ...parts.slice(partIndex + 2)], + obj, + parent, + delimiter + ); + } + } + + static getTreeNodeObject( + nodeTree: TreeNode[], + id: string + ): TreeNode { + for (let i = 0; i < nodeTree.length; i++) { + if (nodeTree[i].node.id === id) { + return nodeTree[i]; + } else if (nodeTree[i].children != null) { + const node = ServiceUtils.getTreeNodeObject(nodeTree[i].children, id); + if (node !== null) { + return node; + } + } + } + return null; + } +} diff --git a/jslib/common/src/misc/throttle.ts b/jslib/common/src/misc/throttle.ts new file mode 100644 index 00000000..852ee999 --- /dev/null +++ b/jslib/common/src/misc/throttle.ts @@ -0,0 +1,69 @@ +/** + * Use as a Decorator on async functions, it will limit how many times the function can be + * in-flight at a time. + * + * Calls beyond the limit will be queued, and run when one of the active calls finishes + */ +export function throttle(limit: number, throttleKey: (args: any[]) => string) { + return ( + target: any, + propertyKey: string | symbol, + descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise> + ) => { + const originalMethod: () => Promise = descriptor.value; + const allThrottles = new Map void)[]>>(); + + const getThrottles = (obj: any) => { + let throttles = allThrottles.get(obj); + if (throttles != null) { + return throttles; + } + throttles = new Map void)[]>(); + allThrottles.set(obj, throttles); + return throttles; + }; + + return { + value: function (...args: any[]) { + const throttles = getThrottles(this); + const argsThrottleKey = throttleKey(args); + let queue = throttles.get(argsThrottleKey); + if (queue == null) { + queue = []; + throttles.set(argsThrottleKey, queue); + } + + return new Promise((resolve, reject) => { + const exec = () => { + const onFinally = () => { + queue.splice(queue.indexOf(exec), 1); + if (queue.length >= limit) { + queue[limit - 1](); + } else if (queue.length === 0) { + throttles.delete(argsThrottleKey); + if (throttles.size === 0) { + allThrottles.delete(this); + } + } + }; + originalMethod + .apply(this, args) + .then((val: any) => { + onFinally(); + return val; + }) + .catch((err: any) => { + onFinally(); + throw err; + }) + .then(resolve, reject); + }; + queue.push(exec); + if (queue.length <= limit) { + exec(); + } + }); + }, + }; + }; +} diff --git a/jslib/common/src/misc/tldjs.noop.ts b/jslib/common/src/misc/tldjs.noop.ts new file mode 100644 index 00000000..b6273a61 --- /dev/null +++ b/jslib/common/src/misc/tldjs.noop.ts @@ -0,0 +1,7 @@ +export function getDomain(host: string): string | null { + return null; +} + +export function isValid(host: string): boolean { + return true; +} diff --git a/jslib/common/src/misc/utils.ts b/jslib/common/src/misc/utils.ts new file mode 100644 index 00000000..1d96e082 --- /dev/null +++ b/jslib/common/src/misc/utils.ts @@ -0,0 +1,406 @@ +/* eslint-disable no-useless-escape */ +import * as tldjs from "tldjs"; + +import { I18nService } from "../abstractions/i18n.service"; + +const nodeURL = typeof window === "undefined" ? require("url") : null; + +export class Utils { + static inited = false; + static isNode = false; + static isBrowser = true; + static isMobileBrowser = false; + static isAppleMobileBrowser = false; + static global: any = null; + static tldEndingRegex = + /.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/; + // Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers. + static regexpEmojiPresentation = + /(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])/g; + + static init() { + if (Utils.inited) { + return; + } + + Utils.inited = true; + Utils.isNode = + typeof process !== "undefined" && + (process as any).release != null && + (process as any).release.name === "node"; + Utils.isBrowser = typeof window !== "undefined"; + Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window); + Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window); + Utils.global = Utils.isNode && !Utils.isBrowser ? global : window; + } + + static fromB64ToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "base64")); + } else { + const binaryString = window.atob(str); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; + } + } + + static fromUrlB64ToArray(str: string): Uint8Array { + return Utils.fromB64ToArray(Utils.fromUrlB64ToB64(str)); + } + + static fromHexToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "hex")); + } else { + const bytes = new Uint8Array(str.length / 2); + for (let i = 0; i < str.length; i += 2) { + bytes[i / 2] = parseInt(str.substr(i, 2), 16); + } + return bytes; + } + } + + static fromUtf8ToArray(str: string): Uint8Array { + if (Utils.isNode) { + return new Uint8Array(Buffer.from(str, "utf8")); + } else { + const strUtf8 = unescape(encodeURIComponent(str)); + const arr = new Uint8Array(strUtf8.length); + for (let i = 0; i < strUtf8.length; i++) { + arr[i] = strUtf8.charCodeAt(i); + } + return arr; + } + } + + static fromByteStringToArray(str: string): Uint8Array { + const arr = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + arr[i] = str.charCodeAt(i); + } + return arr; + } + + static fromBufferToB64(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("base64"); + } else { + let binary = ""; + const bytes = new Uint8Array(buffer); + for (let i = 0; i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]); + } + return window.btoa(binary); + } + } + + static fromBufferToUrlB64(buffer: ArrayBuffer): string { + return Utils.fromB64toUrlB64(Utils.fromBufferToB64(buffer)); + } + + static fromB64toUrlB64(b64Str: string) { + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + + static fromBufferToUtf8(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("utf8"); + } else { + const bytes = new Uint8Array(buffer); + const encodedString = String.fromCharCode.apply(null, bytes); + return decodeURIComponent(escape(encodedString)); + } + } + + static fromBufferToByteString(buffer: ArrayBuffer): string { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); + } + + // ref: https://stackoverflow.com/a/40031979/1090359 + static fromBufferToHex(buffer: ArrayBuffer): string { + if (Utils.isNode) { + return Buffer.from(buffer).toString("hex"); + } else { + const bytes = new Uint8Array(buffer); + return Array.prototype.map + .call(bytes, (x: number) => ("00" + x.toString(16)).slice(-2)) + .join(""); + } + } + + static fromUrlB64ToB64(urlB64Str: string): string { + let output = urlB64Str.replace(/-/g, "+").replace(/_/g, "/"); + switch (output.length % 4) { + case 0: + break; + case 2: + output += "=="; + break; + case 3: + output += "="; + break; + default: + throw new Error("Illegal base64url string!"); + } + + return output; + } + + static fromUrlB64ToUtf8(urlB64Str: string): string { + return Utils.fromB64ToUtf8(Utils.fromUrlB64ToB64(urlB64Str)); + } + + static fromUtf8ToB64(utfStr: string): string { + if (Utils.isNode) { + return Buffer.from(utfStr, "utf8").toString("base64"); + } else { + return decodeURIComponent(escape(window.btoa(utfStr))); + } + } + + static fromUtf8ToUrlB64(utfStr: string): string { + return Utils.fromBufferToUrlB64(Utils.fromUtf8ToArray(utfStr)); + } + + static fromB64ToUtf8(b64Str: string): string { + if (Utils.isNode) { + return Buffer.from(b64Str, "base64").toString("utf8"); + } else { + return decodeURIComponent(escape(window.atob(b64Str))); + } + } + + // ref: http://stackoverflow.com/a/2117523/1090359 + static newGuid(): string { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + static isGuid(id: string) { + return RegExp( + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, + "i" + ).test(id); + } + + static getHostname(uriString: string): string { + const url = Utils.getUrl(uriString); + try { + return url != null && url.hostname !== "" ? url.hostname : null; + } catch { + return null; + } + } + + static getHost(uriString: string): string { + const url = Utils.getUrl(uriString); + try { + return url != null && url.host !== "" ? url.host : null; + } catch { + return null; + } + } + + static getDomain(uriString: string): string { + if (uriString == null) { + return null; + } + + uriString = uriString.trim(); + if (uriString === "") { + return null; + } + + if (uriString.startsWith("data:")) { + return null; + } + + let httpUrl = uriString.startsWith("http://") || uriString.startsWith("https://"); + if ( + !httpUrl && + uriString.indexOf("://") < 0 && + Utils.tldEndingRegex.test(uriString) && + uriString.indexOf("@") < 0 + ) { + uriString = "http://" + uriString; + httpUrl = true; + } + + if (httpUrl) { + try { + const url = Utils.getUrlObject(uriString); + const validHostname = tldjs?.isValid != null ? tldjs.isValid(url.hostname) : true; + if (!validHostname) { + return null; + } + + if (url.hostname === "localhost" || Utils.validIpAddress(url.hostname)) { + return url.hostname; + } + + const urlDomain = + tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(url.hostname) : null; + return urlDomain != null ? urlDomain : url.hostname; + } catch (e) { + // Invalid domain, try another approach below. + } + } + + try { + const domain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(uriString) : null; + + if (domain != null) { + return domain; + } + } catch { + return null; + } + + return null; + } + + static getQueryParams(uriString: string): Map { + const url = Utils.getUrl(uriString); + if (url == null || url.search == null || url.search === "") { + return null; + } + const map = new Map(); + const pairs = (url.search[0] === "?" ? url.search.substr(1) : url.search).split("&"); + pairs.forEach((pair) => { + const parts = pair.split("="); + if (parts.length < 1) { + return; + } + map.set( + decodeURIComponent(parts[0]).toLowerCase(), + parts[1] == null ? "" : decodeURIComponent(parts[1]) + ); + }); + return map; + } + + static getSortFunction(i18nService: I18nService, prop: string) { + return (a: any, b: any) => { + if (a[prop] == null && b[prop] != null) { + return -1; + } + if (a[prop] != null && b[prop] == null) { + return 1; + } + if (a[prop] == null && b[prop] == null) { + return 0; + } + + return i18nService.collator + ? i18nService.collator.compare(a[prop], b[prop]) + : a[prop].localeCompare(b[prop]); + }; + } + + static isNullOrWhitespace(str: string): boolean { + return str == null || typeof str !== "string" || str.trim() === ""; + } + + static isNullOrEmpty(str: string): boolean { + return str == null || typeof str !== "string" || str == ""; + } + + static nameOf(name: string & keyof T) { + return name; + } + + static assign(target: T, source: Partial): T { + return Object.assign(target, source); + } + + static iterateEnum(obj: O) { + return (Object.keys(obj).filter((k) => Number.isNaN(+k)) as K[]).map((k) => obj[k]); + } + + static getUrl(uriString: string): URL { + if (uriString == null) { + return null; + } + + uriString = uriString.trim(); + if (uriString === "") { + return null; + } + + let url = Utils.getUrlObject(uriString); + if (url == null) { + const hasHttpProtocol = + uriString.indexOf("http://") === 0 || uriString.indexOf("https://") === 0; + if (!hasHttpProtocol && uriString.indexOf(".") > -1) { + url = Utils.getUrlObject("http://" + uriString); + } + } + return url; + } + + static camelToPascalCase(s: string) { + return s.charAt(0).toUpperCase() + s.slice(1); + } + + private static validIpAddress(ipString: string): boolean { + const ipRegex = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + return ipRegex.test(ipString); + } + + private static isMobile(win: Window) { + let mobile = false; + ((a) => { + if ( + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( + a + ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( + a.substr(0, 4) + ) + ) { + mobile = true; + } + })(win.navigator.userAgent || win.navigator.vendor || (win as any).opera); + return mobile || win.navigator.userAgent.match(/iPad/i) != null; + } + + private static isAppleMobile(win: Window) { + return ( + win.navigator.userAgent.match(/iPhone/i) != null || + win.navigator.userAgent.match(/iPad/i) != null + ); + } + + private static getUrlObject(uriString: string): URL { + try { + if (nodeURL != null) { + return new nodeURL.URL(uriString); + } else if (typeof URL === "function") { + return new URL(uriString); + } else if (window != null) { + const hasProtocol = uriString.indexOf("://") > -1; + if (!hasProtocol && uriString.indexOf(".") > -1) { + uriString = "http://" + uriString; + } else if (!hasProtocol) { + return null; + } + const anchor = window.document.createElement("a"); + anchor.href = uriString; + return anchor as any; + } + } catch (e) { + // Ignore error + } + + return null; + } +} + +Utils.init(); diff --git a/jslib/common/src/misc/webauthn_iframe.ts b/jslib/common/src/misc/webauthn_iframe.ts new file mode 100644 index 00000000..969dde04 --- /dev/null +++ b/jslib/common/src/misc/webauthn_iframe.ts @@ -0,0 +1,106 @@ +import { I18nService } from "../abstractions/i18n.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; + +export class WebAuthnIFrame { + private iframe: HTMLIFrameElement = null; + private connectorLink: HTMLAnchorElement; + private parseFunction = this.parseMessage.bind(this); + + constructor( + private win: Window, + private webVaultUrl: string, + private webAuthnNewTab: boolean, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private successCallback: Function, // eslint-disable-line + private errorCallback: Function, // eslint-disable-line + private infoCallback: Function // eslint-disable-line + ) { + this.connectorLink = win.document.createElement("a"); + } + + init(data: any): void { + const params = new URLSearchParams({ + data: this.base64Encode(JSON.stringify(data)), + parent: encodeURIComponent(this.win.document.location.href), + btnText: encodeURIComponent(this.i18nService.t("webAuthnAuthenticate")), + v: "1", + }); + + if (this.webAuthnNewTab) { + // Firefox fallback which opens the webauthn page in a new tab + params.append("locale", this.i18nService.translationLocale); + this.platformUtilsService.launchUri( + `${this.webVaultUrl}/webauthn-fallback-connector.html?${params}` + ); + } else { + this.connectorLink.href = `${this.webVaultUrl}/webauthn-connector.html?${params}`; + this.iframe = this.win.document.getElementById("webauthn_iframe") as HTMLIFrameElement; + this.iframe.allow = "publickey-credentials-get " + new URL(this.webVaultUrl).origin; + this.iframe.src = this.connectorLink.href; + + this.win.addEventListener("message", this.parseFunction, false); + } + } + + stop() { + this.sendMessage("stop"); + } + + start() { + this.sendMessage("start"); + } + + sendMessage(message: any) { + if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) { + return; + } + + this.iframe.contentWindow.postMessage(message, this.iframe.src); + } + + base64Encode(str: string): string { + return btoa( + encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => { + return String.fromCharCode(("0x" + p1) as any); + }) + ); + } + + cleanup() { + this.win.removeEventListener("message", this.parseFunction, false); + } + + private parseMessage(event: MessageEvent) { + if (!this.validMessage(event)) { + return; + } + + const parts: string[] = event.data.split("|"); + if (parts[0] === "success" && this.successCallback) { + this.successCallback(parts[1]); + } else if (parts[0] === "error" && this.errorCallback) { + this.errorCallback(parts[1]); + } else if (parts[0] === "info" && this.infoCallback) { + this.infoCallback(parts[1]); + } + } + + private validMessage(event: MessageEvent) { + if ( + event.origin == null || + event.origin === "" || + event.origin !== (this.connectorLink as any).origin || + event.data == null || + typeof event.data !== "string" + ) { + return false; + } + + return ( + event.data.indexOf("success|") === 0 || + event.data.indexOf("error|") === 0 || + event.data.indexOf("info|") === 0 + ); + } +} diff --git a/jslib/common/src/misc/wordlist.ts b/jslib/common/src/misc/wordlist.ts new file mode 100644 index 00000000..cdd30110 --- /dev/null +++ b/jslib/common/src/misc/wordlist.ts @@ -0,0 +1,7779 @@ +// EFF's Long Wordlist from https://www.eff.org/dice +export const EEFLongWordList = [ + "abacus", + "abdomen", + "abdominal", + "abide", + "abiding", + "ability", + "ablaze", + "able", + "abnormal", + "abrasion", + "abrasive", + "abreast", + "abridge", + "abroad", + "abruptly", + "absence", + "absentee", + "absently", + "absinthe", + "absolute", + "absolve", + "abstain", + "abstract", + "absurd", + "accent", + "acclaim", + "acclimate", + "accompany", + "account", + "accuracy", + "accurate", + "accustom", + "acetone", + "achiness", + "aching", + "acid", + "acorn", + "acquaint", + "acquire", + "acre", + "acrobat", + "acronym", + "acting", + "action", + "activate", + "activator", + "active", + "activism", + "activist", + "activity", + "actress", + "acts", + "acutely", + "acuteness", + "aeration", + "aerobics", + "aerosol", + "aerospace", + "afar", + "affair", + "affected", + "affecting", + "affection", + "affidavit", + "affiliate", + "affirm", + "affix", + "afflicted", + "affluent", + "afford", + "affront", + "aflame", + "afloat", + "aflutter", + "afoot", + "afraid", + "afterglow", + "afterlife", + "aftermath", + "aftermost", + "afternoon", + "aged", + "ageless", + "agency", + "agenda", + "agent", + "aggregate", + "aghast", + "agile", + "agility", + "aging", + "agnostic", + "agonize", + "agonizing", + "agony", + "agreeable", + "agreeably", + "agreed", + "agreeing", + "agreement", + "aground", + "ahead", + "ahoy", + "aide", + "aids", + "aim", + "ajar", + "alabaster", + "alarm", + "albatross", + "album", + "alfalfa", + "algebra", + "algorithm", + "alias", + "alibi", + "alienable", + "alienate", + "aliens", + "alike", + "alive", + "alkaline", + "alkalize", + "almanac", + "almighty", + "almost", + "aloe", + "aloft", + "aloha", + "alone", + "alongside", + "aloof", + "alphabet", + "alright", + "although", + "altitude", + "alto", + "aluminum", + "alumni", + "always", + "amaretto", + "amaze", + "amazingly", + "amber", + "ambiance", + "ambiguity", + "ambiguous", + "ambition", + "ambitious", + "ambulance", + "ambush", + "amendable", + "amendment", + "amends", + "amenity", + "amiable", + "amicably", + "amid", + "amigo", + "amino", + "amiss", + "ammonia", + "ammonium", + "amnesty", + "amniotic", + "among", + "amount", + "amperage", + "ample", + "amplifier", + "amplify", + "amply", + "amuck", + "amulet", + "amusable", + "amused", + "amusement", + "amuser", + "amusing", + "anaconda", + "anaerobic", + "anagram", + "anatomist", + "anatomy", + "anchor", + "anchovy", + "ancient", + "android", + "anemia", + "anemic", + "aneurism", + "anew", + "angelfish", + "angelic", + "anger", + "angled", + "angler", + "angles", + "angling", + "angrily", + "angriness", + "anguished", + "angular", + "animal", + "animate", + "animating", + "animation", + "animator", + "anime", + "animosity", + "ankle", + "annex", + "annotate", + "announcer", + "annoying", + "annually", + "annuity", + "anointer", + "another", + "answering", + "antacid", + "antarctic", + "anteater", + "antelope", + "antennae", + "anthem", + "anthill", + "anthology", + "antibody", + "antics", + "antidote", + "antihero", + "antiquely", + "antiques", + "antiquity", + "antirust", + "antitoxic", + "antitrust", + "antiviral", + "antivirus", + "antler", + "antonym", + "antsy", + "anvil", + "anybody", + "anyhow", + "anymore", + "anyone", + "anyplace", + "anything", + "anytime", + "anyway", + "anywhere", + "aorta", + "apache", + "apostle", + "appealing", + "appear", + "appease", + "appeasing", + "appendage", + "appendix", + "appetite", + "appetizer", + "applaud", + "applause", + "apple", + "appliance", + "applicant", + "applied", + "apply", + "appointee", + "appraisal", + "appraiser", + "apprehend", + "approach", + "approval", + "approve", + "apricot", + "april", + "apron", + "aptitude", + "aptly", + "aqua", + "aqueduct", + "arbitrary", + "arbitrate", + "ardently", + "area", + "arena", + "arguable", + "arguably", + "argue", + "arise", + "armadillo", + "armband", + "armchair", + "armed", + "armful", + "armhole", + "arming", + "armless", + "armoire", + "armored", + "armory", + "armrest", + "army", + "aroma", + "arose", + "around", + "arousal", + "arrange", + "array", + "arrest", + "arrival", + "arrive", + "arrogance", + "arrogant", + "arson", + "art", + "ascend", + "ascension", + "ascent", + "ascertain", + "ashamed", + "ashen", + "ashes", + "ashy", + "aside", + "askew", + "asleep", + "asparagus", + "aspect", + "aspirate", + "aspire", + "aspirin", + "astonish", + "astound", + "astride", + "astrology", + "astronaut", + "astronomy", + "astute", + "atlantic", + "atlas", + "atom", + "atonable", + "atop", + "atrium", + "atrocious", + "atrophy", + "attach", + "attain", + "attempt", + "attendant", + "attendee", + "attention", + "attentive", + "attest", + "attic", + "attire", + "attitude", + "attractor", + "attribute", + "atypical", + "auction", + "audacious", + "audacity", + "audible", + "audibly", + "audience", + "audio", + "audition", + "augmented", + "august", + "authentic", + "author", + "autism", + "autistic", + "autograph", + "automaker", + "automated", + "automatic", + "autopilot", + "available", + "avalanche", + "avatar", + "avenge", + "avenging", + "avenue", + "average", + "aversion", + "avert", + "aviation", + "aviator", + "avid", + "avoid", + "await", + "awaken", + "award", + "aware", + "awhile", + "awkward", + "awning", + "awoke", + "awry", + "axis", + "babble", + "babbling", + "babied", + "baboon", + "backache", + "backboard", + "backboned", + "backdrop", + "backed", + "backer", + "backfield", + "backfire", + "backhand", + "backing", + "backlands", + "backlash", + "backless", + "backlight", + "backlit", + "backlog", + "backpack", + "backpedal", + "backrest", + "backroom", + "backshift", + "backside", + "backslid", + "backspace", + "backspin", + "backstab", + "backstage", + "backtalk", + "backtrack", + "backup", + "backward", + "backwash", + "backwater", + "backyard", + "bacon", + "bacteria", + "bacterium", + "badass", + "badge", + "badland", + "badly", + "badness", + "baffle", + "baffling", + "bagel", + "bagful", + "baggage", + "bagged", + "baggie", + "bagginess", + "bagging", + "baggy", + "bagpipe", + "baguette", + "baked", + "bakery", + "bakeshop", + "baking", + "balance", + "balancing", + "balcony", + "balmy", + "balsamic", + "bamboo", + "banana", + "banish", + "banister", + "banjo", + "bankable", + "bankbook", + "banked", + "banker", + "banking", + "banknote", + "bankroll", + "banner", + "bannister", + "banshee", + "banter", + "barbecue", + "barbed", + "barbell", + "barber", + "barcode", + "barge", + "bargraph", + "barista", + "baritone", + "barley", + "barmaid", + "barman", + "barn", + "barometer", + "barrack", + "barracuda", + "barrel", + "barrette", + "barricade", + "barrier", + "barstool", + "bartender", + "barterer", + "bash", + "basically", + "basics", + "basil", + "basin", + "basis", + "basket", + "batboy", + "batch", + "bath", + "baton", + "bats", + "battalion", + "battered", + "battering", + "battery", + "batting", + "battle", + "bauble", + "bazooka", + "blabber", + "bladder", + "blade", + "blah", + "blame", + "blaming", + "blanching", + "blandness", + "blank", + "blaspheme", + "blasphemy", + "blast", + "blatancy", + "blatantly", + "blazer", + "blazing", + "bleach", + "bleak", + "bleep", + "blemish", + "blend", + "bless", + "blighted", + "blimp", + "bling", + "blinked", + "blinker", + "blinking", + "blinks", + "blip", + "blissful", + "blitz", + "blizzard", + "bloated", + "bloating", + "blob", + "blog", + "bloomers", + "blooming", + "blooper", + "blot", + "blouse", + "blubber", + "bluff", + "bluish", + "blunderer", + "blunt", + "blurb", + "blurred", + "blurry", + "blurt", + "blush", + "blustery", + "boaster", + "boastful", + "boasting", + "boat", + "bobbed", + "bobbing", + "bobble", + "bobcat", + "bobsled", + "bobtail", + "bodacious", + "body", + "bogged", + "boggle", + "bogus", + "boil", + "bok", + "bolster", + "bolt", + "bonanza", + "bonded", + "bonding", + "bondless", + "boned", + "bonehead", + "boneless", + "bonelike", + "boney", + "bonfire", + "bonnet", + "bonsai", + "bonus", + "bony", + "boogeyman", + "boogieman", + "book", + "boondocks", + "booted", + "booth", + "bootie", + "booting", + "bootlace", + "bootleg", + "boots", + "boozy", + "borax", + "boring", + "borough", + "borrower", + "borrowing", + "boss", + "botanical", + "botanist", + "botany", + "botch", + "both", + "bottle", + "bottling", + "bottom", + "bounce", + "bouncing", + "bouncy", + "bounding", + "boundless", + "bountiful", + "bovine", + "boxcar", + "boxer", + "boxing", + "boxlike", + "boxy", + "breach", + "breath", + "breeches", + "breeching", + "breeder", + "breeding", + "breeze", + "breezy", + "brethren", + "brewery", + "brewing", + "briar", + "bribe", + "brick", + "bride", + "bridged", + "brigade", + "bright", + "brilliant", + "brim", + "bring", + "brink", + "brisket", + "briskly", + "briskness", + "bristle", + "brittle", + "broadband", + "broadcast", + "broaden", + "broadly", + "broadness", + "broadside", + "broadways", + "broiler", + "broiling", + "broken", + "broker", + "bronchial", + "bronco", + "bronze", + "bronzing", + "brook", + "broom", + "brought", + "browbeat", + "brownnose", + "browse", + "browsing", + "bruising", + "brunch", + "brunette", + "brunt", + "brush", + "brussels", + "brute", + "brutishly", + "bubble", + "bubbling", + "bubbly", + "buccaneer", + "bucked", + "bucket", + "buckle", + "buckshot", + "buckskin", + "bucktooth", + "buckwheat", + "buddhism", + "buddhist", + "budding", + "buddy", + "budget", + "buffalo", + "buffed", + "buffer", + "buffing", + "buffoon", + "buggy", + "bulb", + "bulge", + "bulginess", + "bulgur", + "bulk", + "bulldog", + "bulldozer", + "bullfight", + "bullfrog", + "bullhorn", + "bullion", + "bullish", + "bullpen", + "bullring", + "bullseye", + "bullwhip", + "bully", + "bunch", + "bundle", + "bungee", + "bunion", + "bunkbed", + "bunkhouse", + "bunkmate", + "bunny", + "bunt", + "busboy", + "bush", + "busily", + "busload", + "bust", + "busybody", + "buzz", + "cabana", + "cabbage", + "cabbie", + "cabdriver", + "cable", + "caboose", + "cache", + "cackle", + "cacti", + "cactus", + "caddie", + "caddy", + "cadet", + "cadillac", + "cadmium", + "cage", + "cahoots", + "cake", + "calamari", + "calamity", + "calcium", + "calculate", + "calculus", + "caliber", + "calibrate", + "calm", + "caloric", + "calorie", + "calzone", + "camcorder", + "cameo", + "camera", + "camisole", + "camper", + "campfire", + "camping", + "campsite", + "campus", + "canal", + "canary", + "cancel", + "candied", + "candle", + "candy", + "cane", + "canine", + "canister", + "cannabis", + "canned", + "canning", + "cannon", + "cannot", + "canola", + "canon", + "canopener", + "canopy", + "canteen", + "canyon", + "capable", + "capably", + "capacity", + "cape", + "capillary", + "capital", + "capitol", + "capped", + "capricorn", + "capsize", + "capsule", + "caption", + "captivate", + "captive", + "captivity", + "capture", + "caramel", + "carat", + "caravan", + "carbon", + "cardboard", + "carded", + "cardiac", + "cardigan", + "cardinal", + "cardstock", + "carefully", + "caregiver", + "careless", + "caress", + "caretaker", + "cargo", + "caring", + "carless", + "carload", + "carmaker", + "carnage", + "carnation", + "carnival", + "carnivore", + "carol", + "carpenter", + "carpentry", + "carpool", + "carport", + "carried", + "carrot", + "carrousel", + "carry", + "cartel", + "cartload", + "carton", + "cartoon", + "cartridge", + "cartwheel", + "carve", + "carving", + "carwash", + "cascade", + "case", + "cash", + "casing", + "casino", + "casket", + "cassette", + "casually", + "casualty", + "catacomb", + "catalog", + "catalyst", + "catalyze", + "catapult", + "cataract", + "catatonic", + "catcall", + "catchable", + "catcher", + "catching", + "catchy", + "caterer", + "catering", + "catfight", + "catfish", + "cathedral", + "cathouse", + "catlike", + "catnap", + "catnip", + "catsup", + "cattail", + "cattishly", + "cattle", + "catty", + "catwalk", + "caucasian", + "caucus", + "causal", + "causation", + "cause", + "causing", + "cauterize", + "caution", + "cautious", + "cavalier", + "cavalry", + "caviar", + "cavity", + "cedar", + "celery", + "celestial", + "celibacy", + "celibate", + "celtic", + "cement", + "census", + "ceramics", + "ceremony", + "certainly", + "certainty", + "certified", + "certify", + "cesarean", + "cesspool", + "chafe", + "chaffing", + "chain", + "chair", + "chalice", + "challenge", + "chamber", + "chamomile", + "champion", + "chance", + "change", + "channel", + "chant", + "chaos", + "chaperone", + "chaplain", + "chapped", + "chaps", + "chapter", + "character", + "charbroil", + "charcoal", + "charger", + "charging", + "chariot", + "charity", + "charm", + "charred", + "charter", + "charting", + "chase", + "chasing", + "chaste", + "chastise", + "chastity", + "chatroom", + "chatter", + "chatting", + "chatty", + "cheating", + "cheddar", + "cheek", + "cheer", + "cheese", + "cheesy", + "chef", + "chemicals", + "chemist", + "chemo", + "cherisher", + "cherub", + "chess", + "chest", + "chevron", + "chevy", + "chewable", + "chewer", + "chewing", + "chewy", + "chief", + "chihuahua", + "childcare", + "childhood", + "childish", + "childless", + "childlike", + "chili", + "chill", + "chimp", + "chip", + "chirping", + "chirpy", + "chitchat", + "chivalry", + "chive", + "chloride", + "chlorine", + "choice", + "chokehold", + "choking", + "chomp", + "chooser", + "choosing", + "choosy", + "chop", + "chosen", + "chowder", + "chowtime", + "chrome", + "chubby", + "chuck", + "chug", + "chummy", + "chump", + "chunk", + "churn", + "chute", + "cider", + "cilantro", + "cinch", + "cinema", + "cinnamon", + "circle", + "circling", + "circular", + "circulate", + "circus", + "citable", + "citadel", + "citation", + "citizen", + "citric", + "citrus", + "city", + "civic", + "civil", + "clad", + "claim", + "clambake", + "clammy", + "clamor", + "clamp", + "clamshell", + "clang", + "clanking", + "clapped", + "clapper", + "clapping", + "clarify", + "clarinet", + "clarity", + "clash", + "clasp", + "class", + "clatter", + "clause", + "clavicle", + "claw", + "clay", + "clean", + "clear", + "cleat", + "cleaver", + "cleft", + "clench", + "clergyman", + "clerical", + "clerk", + "clever", + "clicker", + "client", + "climate", + "climatic", + "cling", + "clinic", + "clinking", + "clip", + "clique", + "cloak", + "clobber", + "clock", + "clone", + "cloning", + "closable", + "closure", + "clothes", + "clothing", + "cloud", + "clover", + "clubbed", + "clubbing", + "clubhouse", + "clump", + "clumsily", + "clumsy", + "clunky", + "clustered", + "clutch", + "clutter", + "coach", + "coagulant", + "coastal", + "coaster", + "coasting", + "coastland", + "coastline", + "coat", + "coauthor", + "cobalt", + "cobbler", + "cobweb", + "cocoa", + "coconut", + "cod", + "coeditor", + "coerce", + "coexist", + "coffee", + "cofounder", + "cognition", + "cognitive", + "cogwheel", + "coherence", + "coherent", + "cohesive", + "coil", + "coke", + "cola", + "cold", + "coleslaw", + "coliseum", + "collage", + "collapse", + "collar", + "collected", + "collector", + "collide", + "collie", + "collision", + "colonial", + "colonist", + "colonize", + "colony", + "colossal", + "colt", + "coma", + "come", + "comfort", + "comfy", + "comic", + "coming", + "comma", + "commence", + "commend", + "comment", + "commerce", + "commode", + "commodity", + "commodore", + "common", + "commotion", + "commute", + "commuting", + "compacted", + "compacter", + "compactly", + "compactor", + "companion", + "company", + "compare", + "compel", + "compile", + "comply", + "component", + "composed", + "composer", + "composite", + "compost", + "composure", + "compound", + "compress", + "comprised", + "computer", + "computing", + "comrade", + "concave", + "conceal", + "conceded", + "concept", + "concerned", + "concert", + "conch", + "concierge", + "concise", + "conclude", + "concrete", + "concur", + "condense", + "condiment", + "condition", + "condone", + "conducive", + "conductor", + "conduit", + "cone", + "confess", + "confetti", + "confidant", + "confident", + "confider", + "confiding", + "configure", + "confined", + "confining", + "confirm", + "conflict", + "conform", + "confound", + "confront", + "confused", + "confusing", + "confusion", + "congenial", + "congested", + "congrats", + "congress", + "conical", + "conjoined", + "conjure", + "conjuror", + "connected", + "connector", + "consensus", + "consent", + "console", + "consoling", + "consonant", + "constable", + "constant", + "constrain", + "constrict", + "construct", + "consult", + "consumer", + "consuming", + "contact", + "container", + "contempt", + "contend", + "contented", + "contently", + "contents", + "contest", + "context", + "contort", + "contour", + "contrite", + "control", + "contusion", + "convene", + "convent", + "copartner", + "cope", + "copied", + "copier", + "copilot", + "coping", + "copious", + "copper", + "copy", + "coral", + "cork", + "cornball", + "cornbread", + "corncob", + "cornea", + "corned", + "corner", + "cornfield", + "cornflake", + "cornhusk", + "cornmeal", + "cornstalk", + "corny", + "coronary", + "coroner", + "corporal", + "corporate", + "corral", + "correct", + "corridor", + "corrode", + "corroding", + "corrosive", + "corsage", + "corset", + "cortex", + "cosigner", + "cosmetics", + "cosmic", + "cosmos", + "cosponsor", + "cost", + "cottage", + "cotton", + "couch", + "cough", + "could", + "countable", + "countdown", + "counting", + "countless", + "country", + "county", + "courier", + "covenant", + "cover", + "coveted", + "coveting", + "coyness", + "cozily", + "coziness", + "cozy", + "crabbing", + "crabgrass", + "crablike", + "crabmeat", + "cradle", + "cradling", + "crafter", + "craftily", + "craftsman", + "craftwork", + "crafty", + "cramp", + "cranberry", + "crane", + "cranial", + "cranium", + "crank", + "crate", + "crave", + "craving", + "crawfish", + "crawlers", + "crawling", + "crayfish", + "crayon", + "crazed", + "crazily", + "craziness", + "crazy", + "creamed", + "creamer", + "creamlike", + "crease", + "creasing", + "creatable", + "create", + "creation", + "creative", + "creature", + "credible", + "credibly", + "credit", + "creed", + "creme", + "creole", + "crepe", + "crept", + "crescent", + "crested", + "cresting", + "crestless", + "crevice", + "crewless", + "crewman", + "crewmate", + "crib", + "cricket", + "cried", + "crier", + "crimp", + "crimson", + "cringe", + "cringing", + "crinkle", + "crinkly", + "crisped", + "crisping", + "crisply", + "crispness", + "crispy", + "criteria", + "critter", + "croak", + "crock", + "crook", + "croon", + "crop", + "cross", + "crouch", + "crouton", + "crowbar", + "crowd", + "crown", + "crucial", + "crudely", + "crudeness", + "cruelly", + "cruelness", + "cruelty", + "crumb", + "crummiest", + "crummy", + "crumpet", + "crumpled", + "cruncher", + "crunching", + "crunchy", + "crusader", + "crushable", + "crushed", + "crusher", + "crushing", + "crust", + "crux", + "crying", + "cryptic", + "crystal", + "cubbyhole", + "cube", + "cubical", + "cubicle", + "cucumber", + "cuddle", + "cuddly", + "cufflink", + "culinary", + "culminate", + "culpable", + "culprit", + "cultivate", + "cultural", + "culture", + "cupbearer", + "cupcake", + "cupid", + "cupped", + "cupping", + "curable", + "curator", + "curdle", + "cure", + "curfew", + "curing", + "curled", + "curler", + "curliness", + "curling", + "curly", + "curry", + "curse", + "cursive", + "cursor", + "curtain", + "curtly", + "curtsy", + "curvature", + "curve", + "curvy", + "cushy", + "cusp", + "cussed", + "custard", + "custodian", + "custody", + "customary", + "customer", + "customize", + "customs", + "cut", + "cycle", + "cyclic", + "cycling", + "cyclist", + "cylinder", + "cymbal", + "cytoplasm", + "cytoplast", + "dab", + "dad", + "daffodil", + "dagger", + "daily", + "daintily", + "dainty", + "dairy", + "daisy", + "dallying", + "dance", + "dancing", + "dandelion", + "dander", + "dandruff", + "dandy", + "danger", + "dangle", + "dangling", + "daredevil", + "dares", + "daringly", + "darkened", + "darkening", + "darkish", + "darkness", + "darkroom", + "darling", + "darn", + "dart", + "darwinism", + "dash", + "dastardly", + "data", + "datebook", + "dating", + "daughter", + "daunting", + "dawdler", + "dawn", + "daybed", + "daybreak", + "daycare", + "daydream", + "daylight", + "daylong", + "dayroom", + "daytime", + "dazzler", + "dazzling", + "deacon", + "deafening", + "deafness", + "dealer", + "dealing", + "dealmaker", + "dealt", + "dean", + "debatable", + "debate", + "debating", + "debit", + "debrief", + "debtless", + "debtor", + "debug", + "debunk", + "decade", + "decaf", + "decal", + "decathlon", + "decay", + "deceased", + "deceit", + "deceiver", + "deceiving", + "december", + "decency", + "decent", + "deception", + "deceptive", + "decibel", + "decidable", + "decimal", + "decimeter", + "decipher", + "deck", + "declared", + "decline", + "decode", + "decompose", + "decorated", + "decorator", + "decoy", + "decrease", + "decree", + "dedicate", + "dedicator", + "deduce", + "deduct", + "deed", + "deem", + "deepen", + "deeply", + "deepness", + "deface", + "defacing", + "defame", + "default", + "defeat", + "defection", + "defective", + "defendant", + "defender", + "defense", + "defensive", + "deferral", + "deferred", + "defiance", + "defiant", + "defile", + "defiling", + "define", + "definite", + "deflate", + "deflation", + "deflator", + "deflected", + "deflector", + "defog", + "deforest", + "defraud", + "defrost", + "deftly", + "defuse", + "defy", + "degraded", + "degrading", + "degrease", + "degree", + "dehydrate", + "deity", + "dejected", + "delay", + "delegate", + "delegator", + "delete", + "deletion", + "delicacy", + "delicate", + "delicious", + "delighted", + "delirious", + "delirium", + "deliverer", + "delivery", + "delouse", + "delta", + "deluge", + "delusion", + "deluxe", + "demanding", + "demeaning", + "demeanor", + "demise", + "democracy", + "democrat", + "demote", + "demotion", + "demystify", + "denatured", + "deniable", + "denial", + "denim", + "denote", + "dense", + "density", + "dental", + "dentist", + "denture", + "deny", + "deodorant", + "deodorize", + "departed", + "departure", + "depict", + "deplete", + "depletion", + "deplored", + "deploy", + "deport", + "depose", + "depraved", + "depravity", + "deprecate", + "depress", + "deprive", + "depth", + "deputize", + "deputy", + "derail", + "deranged", + "derby", + "derived", + "desecrate", + "deserve", + "deserving", + "designate", + "designed", + "designer", + "designing", + "deskbound", + "desktop", + "deskwork", + "desolate", + "despair", + "despise", + "despite", + "destiny", + "destitute", + "destruct", + "detached", + "detail", + "detection", + "detective", + "detector", + "detention", + "detergent", + "detest", + "detonate", + "detonator", + "detoxify", + "detract", + "deuce", + "devalue", + "deviancy", + "deviant", + "deviate", + "deviation", + "deviator", + "device", + "devious", + "devotedly", + "devotee", + "devotion", + "devourer", + "devouring", + "devoutly", + "dexterity", + "dexterous", + "diabetes", + "diabetic", + "diabolic", + "diagnoses", + "diagnosis", + "diagram", + "dial", + "diameter", + "diaper", + "diaphragm", + "diary", + "dice", + "dicing", + "dictate", + "dictation", + "dictator", + "difficult", + "diffused", + "diffuser", + "diffusion", + "diffusive", + "dig", + "dilation", + "diligence", + "diligent", + "dill", + "dilute", + "dime", + "diminish", + "dimly", + "dimmed", + "dimmer", + "dimness", + "dimple", + "diner", + "dingbat", + "dinghy", + "dinginess", + "dingo", + "dingy", + "dining", + "dinner", + "diocese", + "dioxide", + "diploma", + "dipped", + "dipper", + "dipping", + "directed", + "direction", + "directive", + "directly", + "directory", + "direness", + "dirtiness", + "disabled", + "disagree", + "disallow", + "disarm", + "disarray", + "disaster", + "disband", + "disbelief", + "disburse", + "discard", + "discern", + "discharge", + "disclose", + "discolor", + "discount", + "discourse", + "discover", + "discuss", + "disdain", + "disengage", + "disfigure", + "disgrace", + "dish", + "disinfect", + "disjoin", + "disk", + "dislike", + "disliking", + "dislocate", + "dislodge", + "disloyal", + "dismantle", + "dismay", + "dismiss", + "dismount", + "disobey", + "disorder", + "disown", + "disparate", + "disparity", + "dispatch", + "dispense", + "dispersal", + "dispersed", + "disperser", + "displace", + "display", + "displease", + "disposal", + "dispose", + "disprove", + "dispute", + "disregard", + "disrupt", + "dissuade", + "distance", + "distant", + "distaste", + "distill", + "distinct", + "distort", + "distract", + "distress", + "district", + "distrust", + "ditch", + "ditto", + "ditzy", + "dividable", + "divided", + "dividend", + "dividers", + "dividing", + "divinely", + "diving", + "divinity", + "divisible", + "divisibly", + "division", + "divisive", + "divorcee", + "dizziness", + "dizzy", + "doable", + "docile", + "dock", + "doctrine", + "document", + "dodge", + "dodgy", + "doily", + "doing", + "dole", + "dollar", + "dollhouse", + "dollop", + "dolly", + "dolphin", + "domain", + "domelike", + "domestic", + "dominion", + "dominoes", + "donated", + "donation", + "donator", + "donor", + "donut", + "doodle", + "doorbell", + "doorframe", + "doorknob", + "doorman", + "doormat", + "doornail", + "doorpost", + "doorstep", + "doorstop", + "doorway", + "doozy", + "dork", + "dormitory", + "dorsal", + "dosage", + "dose", + "dotted", + "doubling", + "douche", + "dove", + "down", + "dowry", + "doze", + "drab", + "dragging", + "dragonfly", + "dragonish", + "dragster", + "drainable", + "drainage", + "drained", + "drainer", + "drainpipe", + "dramatic", + "dramatize", + "drank", + "drapery", + "drastic", + "draw", + "dreaded", + "dreadful", + "dreadlock", + "dreamboat", + "dreamily", + "dreamland", + "dreamless", + "dreamlike", + "dreamt", + "dreamy", + "drearily", + "dreary", + "drench", + "dress", + "drew", + "dribble", + "dried", + "drier", + "drift", + "driller", + "drilling", + "drinkable", + "drinking", + "dripping", + "drippy", + "drivable", + "driven", + "driver", + "driveway", + "driving", + "drizzle", + "drizzly", + "drone", + "drool", + "droop", + "drop-down", + "dropbox", + "dropkick", + "droplet", + "dropout", + "dropper", + "drove", + "drown", + "drowsily", + "drudge", + "drum", + "dry", + "dubbed", + "dubiously", + "duchess", + "duckbill", + "ducking", + "duckling", + "ducktail", + "ducky", + "duct", + "dude", + "duffel", + "dugout", + "duh", + "duke", + "duller", + "dullness", + "duly", + "dumping", + "dumpling", + "dumpster", + "duo", + "dupe", + "duplex", + "duplicate", + "duplicity", + "durable", + "durably", + "duration", + "duress", + "during", + "dusk", + "dust", + "dutiful", + "duty", + "duvet", + "dwarf", + "dweeb", + "dwelled", + "dweller", + "dwelling", + "dwindle", + "dwindling", + "dynamic", + "dynamite", + "dynasty", + "dyslexia", + "dyslexic", + "each", + "eagle", + "earache", + "eardrum", + "earflap", + "earful", + "earlobe", + "early", + "earmark", + "earmuff", + "earphone", + "earpiece", + "earplugs", + "earring", + "earshot", + "earthen", + "earthlike", + "earthling", + "earthly", + "earthworm", + "earthy", + "earwig", + "easeful", + "easel", + "easiest", + "easily", + "easiness", + "easing", + "eastbound", + "eastcoast", + "easter", + "eastward", + "eatable", + "eaten", + "eatery", + "eating", + "eats", + "ebay", + "ebony", + "ebook", + "ecard", + "eccentric", + "echo", + "eclair", + "eclipse", + "ecologist", + "ecology", + "economic", + "economist", + "economy", + "ecosphere", + "ecosystem", + "edge", + "edginess", + "edging", + "edgy", + "edition", + "editor", + "educated", + "education", + "educator", + "eel", + "effective", + "effects", + "efficient", + "effort", + "eggbeater", + "egging", + "eggnog", + "eggplant", + "eggshell", + "egomaniac", + "egotism", + "egotistic", + "either", + "eject", + "elaborate", + "elastic", + "elated", + "elbow", + "eldercare", + "elderly", + "eldest", + "electable", + "election", + "elective", + "elephant", + "elevate", + "elevating", + "elevation", + "elevator", + "eleven", + "elf", + "eligible", + "eligibly", + "eliminate", + "elite", + "elitism", + "elixir", + "elk", + "ellipse", + "elliptic", + "elm", + "elongated", + "elope", + "eloquence", + "eloquent", + "elsewhere", + "elude", + "elusive", + "elves", + "email", + "embargo", + "embark", + "embassy", + "embattled", + "embellish", + "ember", + "embezzle", + "emblaze", + "emblem", + "embody", + "embolism", + "emboss", + "embroider", + "emcee", + "emerald", + "emergency", + "emission", + "emit", + "emote", + "emoticon", + "emotion", + "empathic", + "empathy", + "emperor", + "emphases", + "emphasis", + "emphasize", + "emphatic", + "empirical", + "employed", + "employee", + "employer", + "emporium", + "empower", + "emptier", + "emptiness", + "empty", + "emu", + "enable", + "enactment", + "enamel", + "enchanted", + "enchilada", + "encircle", + "enclose", + "enclosure", + "encode", + "encore", + "encounter", + "encourage", + "encroach", + "encrust", + "encrypt", + "endanger", + "endeared", + "endearing", + "ended", + "ending", + "endless", + "endnote", + "endocrine", + "endorphin", + "endorse", + "endowment", + "endpoint", + "endurable", + "endurance", + "enduring", + "energetic", + "energize", + "energy", + "enforced", + "enforcer", + "engaged", + "engaging", + "engine", + "engorge", + "engraved", + "engraver", + "engraving", + "engross", + "engulf", + "enhance", + "enigmatic", + "enjoyable", + "enjoyably", + "enjoyer", + "enjoying", + "enjoyment", + "enlarged", + "enlarging", + "enlighten", + "enlisted", + "enquirer", + "enrage", + "enrich", + "enroll", + "enslave", + "ensnare", + "ensure", + "entail", + "entangled", + "entering", + "entertain", + "enticing", + "entire", + "entitle", + "entity", + "entomb", + "entourage", + "entrap", + "entree", + "entrench", + "entrust", + "entryway", + "entwine", + "enunciate", + "envelope", + "enviable", + "enviably", + "envious", + "envision", + "envoy", + "envy", + "enzyme", + "epic", + "epidemic", + "epidermal", + "epidermis", + "epidural", + "epilepsy", + "epileptic", + "epilogue", + "epiphany", + "episode", + "equal", + "equate", + "equation", + "equator", + "equinox", + "equipment", + "equity", + "equivocal", + "eradicate", + "erasable", + "erased", + "eraser", + "erasure", + "ergonomic", + "errand", + "errant", + "erratic", + "error", + "erupt", + "escalate", + "escalator", + "escapable", + "escapade", + "escapist", + "escargot", + "eskimo", + "esophagus", + "espionage", + "espresso", + "esquire", + "essay", + "essence", + "essential", + "establish", + "estate", + "esteemed", + "estimate", + "estimator", + "estranged", + "estrogen", + "etching", + "eternal", + "eternity", + "ethanol", + "ether", + "ethically", + "ethics", + "euphemism", + "evacuate", + "evacuee", + "evade", + "evaluate", + "evaluator", + "evaporate", + "evasion", + "evasive", + "even", + "everglade", + "evergreen", + "everybody", + "everyday", + "everyone", + "evict", + "evidence", + "evident", + "evil", + "evoke", + "evolution", + "evolve", + "exact", + "exalted", + "example", + "excavate", + "excavator", + "exceeding", + "exception", + "excess", + "exchange", + "excitable", + "exciting", + "exclaim", + "exclude", + "excluding", + "exclusion", + "exclusive", + "excretion", + "excretory", + "excursion", + "excusable", + "excusably", + "excuse", + "exemplary", + "exemplify", + "exemption", + "exerciser", + "exert", + "exes", + "exfoliate", + "exhale", + "exhaust", + "exhume", + "exile", + "existing", + "exit", + "exodus", + "exonerate", + "exorcism", + "exorcist", + "expand", + "expanse", + "expansion", + "expansive", + "expectant", + "expedited", + "expediter", + "expel", + "expend", + "expenses", + "expensive", + "expert", + "expire", + "expiring", + "explain", + "expletive", + "explicit", + "explode", + "exploit", + "explore", + "exploring", + "exponent", + "exporter", + "exposable", + "expose", + "exposure", + "express", + "expulsion", + "exquisite", + "extended", + "extending", + "extent", + "extenuate", + "exterior", + "external", + "extinct", + "extortion", + "extradite", + "extras", + "extrovert", + "extrude", + "extruding", + "exuberant", + "fable", + "fabric", + "fabulous", + "facebook", + "facecloth", + "facedown", + "faceless", + "facelift", + "faceplate", + "faceted", + "facial", + "facility", + "facing", + "facsimile", + "faction", + "factoid", + "factor", + "factsheet", + "factual", + "faculty", + "fade", + "fading", + "failing", + "falcon", + "fall", + "false", + "falsify", + "fame", + "familiar", + "family", + "famine", + "famished", + "fanatic", + "fancied", + "fanciness", + "fancy", + "fanfare", + "fang", + "fanning", + "fantasize", + "fantastic", + "fantasy", + "fascism", + "fastball", + "faster", + "fasting", + "fastness", + "faucet", + "favorable", + "favorably", + "favored", + "favoring", + "favorite", + "fax", + "feast", + "federal", + "fedora", + "feeble", + "feed", + "feel", + "feisty", + "feline", + "felt-tip", + "feminine", + "feminism", + "feminist", + "feminize", + "femur", + "fence", + "fencing", + "fender", + "ferment", + "fernlike", + "ferocious", + "ferocity", + "ferret", + "ferris", + "ferry", + "fervor", + "fester", + "festival", + "festive", + "festivity", + "fetal", + "fetch", + "fever", + "fiber", + "fiction", + "fiddle", + "fiddling", + "fidelity", + "fidgeting", + "fidgety", + "fifteen", + "fifth", + "fiftieth", + "fifty", + "figment", + "figure", + "figurine", + "filing", + "filled", + "filler", + "filling", + "film", + "filter", + "filth", + "filtrate", + "finale", + "finalist", + "finalize", + "finally", + "finance", + "financial", + "finch", + "fineness", + "finer", + "finicky", + "finished", + "finisher", + "finishing", + "finite", + "finless", + "finlike", + "fiscally", + "fit", + "five", + "flaccid", + "flagman", + "flagpole", + "flagship", + "flagstick", + "flagstone", + "flail", + "flakily", + "flaky", + "flame", + "flammable", + "flanked", + "flanking", + "flannels", + "flap", + "flaring", + "flashback", + "flashbulb", + "flashcard", + "flashily", + "flashing", + "flashy", + "flask", + "flatbed", + "flatfoot", + "flatly", + "flatness", + "flatten", + "flattered", + "flatterer", + "flattery", + "flattop", + "flatware", + "flatworm", + "flavored", + "flavorful", + "flavoring", + "flaxseed", + "fled", + "fleshed", + "fleshy", + "flick", + "flier", + "flight", + "flinch", + "fling", + "flint", + "flip", + "flirt", + "float", + "flock", + "flogging", + "flop", + "floral", + "florist", + "floss", + "flounder", + "flyable", + "flyaway", + "flyer", + "flying", + "flyover", + "flypaper", + "foam", + "foe", + "fog", + "foil", + "folic", + "folk", + "follicle", + "follow", + "fondling", + "fondly", + "fondness", + "fondue", + "font", + "food", + "fool", + "footage", + "football", + "footbath", + "footboard", + "footer", + "footgear", + "foothill", + "foothold", + "footing", + "footless", + "footman", + "footnote", + "footpad", + "footpath", + "footprint", + "footrest", + "footsie", + "footsore", + "footwear", + "footwork", + "fossil", + "foster", + "founder", + "founding", + "fountain", + "fox", + "foyer", + "fraction", + "fracture", + "fragile", + "fragility", + "fragment", + "fragrance", + "fragrant", + "frail", + "frame", + "framing", + "frantic", + "fraternal", + "frayed", + "fraying", + "frays", + "freckled", + "freckles", + "freebase", + "freebee", + "freebie", + "freedom", + "freefall", + "freehand", + "freeing", + "freeload", + "freely", + "freemason", + "freeness", + "freestyle", + "freeware", + "freeway", + "freewill", + "freezable", + "freezing", + "freight", + "french", + "frenzied", + "frenzy", + "frequency", + "frequent", + "fresh", + "fretful", + "fretted", + "friction", + "friday", + "fridge", + "fried", + "friend", + "frighten", + "frightful", + "frigidity", + "frigidly", + "frill", + "fringe", + "frisbee", + "frisk", + "fritter", + "frivolous", + "frolic", + "from", + "front", + "frostbite", + "frosted", + "frostily", + "frosting", + "frostlike", + "frosty", + "froth", + "frown", + "frozen", + "fructose", + "frugality", + "frugally", + "fruit", + "frustrate", + "frying", + "gab", + "gaffe", + "gag", + "gainfully", + "gaining", + "gains", + "gala", + "gallantly", + "galleria", + "gallery", + "galley", + "gallon", + "gallows", + "gallstone", + "galore", + "galvanize", + "gambling", + "game", + "gaming", + "gamma", + "gander", + "gangly", + "gangrene", + "gangway", + "gap", + "garage", + "garbage", + "garden", + "gargle", + "garland", + "garlic", + "garment", + "garnet", + "garnish", + "garter", + "gas", + "gatherer", + "gathering", + "gating", + "gauging", + "gauntlet", + "gauze", + "gave", + "gawk", + "gazing", + "gear", + "gecko", + "geek", + "geiger", + "gem", + "gender", + "generic", + "generous", + "genetics", + "genre", + "gentile", + "gentleman", + "gently", + "gents", + "geography", + "geologic", + "geologist", + "geology", + "geometric", + "geometry", + "geranium", + "gerbil", + "geriatric", + "germicide", + "germinate", + "germless", + "germproof", + "gestate", + "gestation", + "gesture", + "getaway", + "getting", + "getup", + "giant", + "gibberish", + "giblet", + "giddily", + "giddiness", + "giddy", + "gift", + "gigabyte", + "gigahertz", + "gigantic", + "giggle", + "giggling", + "giggly", + "gigolo", + "gilled", + "gills", + "gimmick", + "girdle", + "giveaway", + "given", + "giver", + "giving", + "gizmo", + "gizzard", + "glacial", + "glacier", + "glade", + "gladiator", + "gladly", + "glamorous", + "glamour", + "glance", + "glancing", + "glandular", + "glare", + "glaring", + "glass", + "glaucoma", + "glazing", + "gleaming", + "gleeful", + "glider", + "gliding", + "glimmer", + "glimpse", + "glisten", + "glitch", + "glitter", + "glitzy", + "gloater", + "gloating", + "gloomily", + "gloomy", + "glorified", + "glorifier", + "glorify", + "glorious", + "glory", + "gloss", + "glove", + "glowing", + "glowworm", + "glucose", + "glue", + "gluten", + "glutinous", + "glutton", + "gnarly", + "gnat", + "goal", + "goatskin", + "goes", + "goggles", + "going", + "goldfish", + "goldmine", + "goldsmith", + "golf", + "goliath", + "gonad", + "gondola", + "gone", + "gong", + "good", + "gooey", + "goofball", + "goofiness", + "goofy", + "google", + "goon", + "gopher", + "gore", + "gorged", + "gorgeous", + "gory", + "gosling", + "gossip", + "gothic", + "gotten", + "gout", + "gown", + "grab", + "graceful", + "graceless", + "gracious", + "gradation", + "graded", + "grader", + "gradient", + "grading", + "gradually", + "graduate", + "graffiti", + "grafted", + "grafting", + "grain", + "granddad", + "grandkid", + "grandly", + "grandma", + "grandpa", + "grandson", + "granite", + "granny", + "granola", + "grant", + "granular", + "grape", + "graph", + "grapple", + "grappling", + "grasp", + "grass", + "gratified", + "gratify", + "grating", + "gratitude", + "gratuity", + "gravel", + "graveness", + "graves", + "graveyard", + "gravitate", + "gravity", + "gravy", + "gray", + "grazing", + "greasily", + "greedily", + "greedless", + "greedy", + "green", + "greeter", + "greeting", + "grew", + "greyhound", + "grid", + "grief", + "grievance", + "grieving", + "grievous", + "grill", + "grimace", + "grimacing", + "grime", + "griminess", + "grimy", + "grinch", + "grinning", + "grip", + "gristle", + "grit", + "groggily", + "groggy", + "groin", + "groom", + "groove", + "grooving", + "groovy", + "grope", + "ground", + "grouped", + "grout", + "grove", + "grower", + "growing", + "growl", + "grub", + "grudge", + "grudging", + "grueling", + "gruffly", + "grumble", + "grumbling", + "grumbly", + "grumpily", + "grunge", + "grunt", + "guacamole", + "guidable", + "guidance", + "guide", + "guiding", + "guileless", + "guise", + "gulf", + "gullible", + "gully", + "gulp", + "gumball", + "gumdrop", + "gumminess", + "gumming", + "gummy", + "gurgle", + "gurgling", + "guru", + "gush", + "gusto", + "gusty", + "gutless", + "guts", + "gutter", + "guy", + "guzzler", + "gyration", + "habitable", + "habitant", + "habitat", + "habitual", + "hacked", + "hacker", + "hacking", + "hacksaw", + "had", + "haggler", + "haiku", + "half", + "halogen", + "halt", + "halved", + "halves", + "hamburger", + "hamlet", + "hammock", + "hamper", + "hamster", + "hamstring", + "handbag", + "handball", + "handbook", + "handbrake", + "handcart", + "handclap", + "handclasp", + "handcraft", + "handcuff", + "handed", + "handful", + "handgrip", + "handgun", + "handheld", + "handiness", + "handiwork", + "handlebar", + "handled", + "handler", + "handling", + "handmade", + "handoff", + "handpick", + "handprint", + "handrail", + "handsaw", + "handset", + "handsfree", + "handshake", + "handstand", + "handwash", + "handwork", + "handwoven", + "handwrite", + "handyman", + "hangnail", + "hangout", + "hangover", + "hangup", + "hankering", + "hankie", + "hanky", + "haphazard", + "happening", + "happier", + "happiest", + "happily", + "happiness", + "happy", + "harbor", + "hardcopy", + "hardcore", + "hardcover", + "harddisk", + "hardened", + "hardener", + "hardening", + "hardhat", + "hardhead", + "hardiness", + "hardly", + "hardness", + "hardship", + "hardware", + "hardwired", + "hardwood", + "hardy", + "harmful", + "harmless", + "harmonica", + "harmonics", + "harmonize", + "harmony", + "harness", + "harpist", + "harsh", + "harvest", + "hash", + "hassle", + "haste", + "hastily", + "hastiness", + "hasty", + "hatbox", + "hatchback", + "hatchery", + "hatchet", + "hatching", + "hatchling", + "hate", + "hatless", + "hatred", + "haunt", + "haven", + "hazard", + "hazelnut", + "hazily", + "haziness", + "hazing", + "hazy", + "headache", + "headband", + "headboard", + "headcount", + "headdress", + "headed", + "header", + "headfirst", + "headgear", + "heading", + "headlamp", + "headless", + "headlock", + "headphone", + "headpiece", + "headrest", + "headroom", + "headscarf", + "headset", + "headsman", + "headstand", + "headstone", + "headway", + "headwear", + "heap", + "heat", + "heave", + "heavily", + "heaviness", + "heaving", + "hedge", + "hedging", + "heftiness", + "hefty", + "helium", + "helmet", + "helper", + "helpful", + "helping", + "helpless", + "helpline", + "hemlock", + "hemstitch", + "hence", + "henchman", + "henna", + "herald", + "herbal", + "herbicide", + "herbs", + "heritage", + "hermit", + "heroics", + "heroism", + "herring", + "herself", + "hertz", + "hesitancy", + "hesitant", + "hesitate", + "hexagon", + "hexagram", + "hubcap", + "huddle", + "huddling", + "huff", + "hug", + "hula", + "hulk", + "hull", + "human", + "humble", + "humbling", + "humbly", + "humid", + "humiliate", + "humility", + "humming", + "hummus", + "humongous", + "humorist", + "humorless", + "humorous", + "humpback", + "humped", + "humvee", + "hunchback", + "hundredth", + "hunger", + "hungrily", + "hungry", + "hunk", + "hunter", + "hunting", + "huntress", + "huntsman", + "hurdle", + "hurled", + "hurler", + "hurling", + "hurray", + "hurricane", + "hurried", + "hurry", + "hurt", + "husband", + "hush", + "husked", + "huskiness", + "hut", + "hybrid", + "hydrant", + "hydrated", + "hydration", + "hydrogen", + "hydroxide", + "hyperlink", + "hypertext", + "hyphen", + "hypnoses", + "hypnosis", + "hypnotic", + "hypnotism", + "hypnotist", + "hypnotize", + "hypocrisy", + "hypocrite", + "ibuprofen", + "ice", + "iciness", + "icing", + "icky", + "icon", + "icy", + "idealism", + "idealist", + "idealize", + "ideally", + "idealness", + "identical", + "identify", + "identity", + "ideology", + "idiocy", + "idiom", + "idly", + "igloo", + "ignition", + "ignore", + "iguana", + "illicitly", + "illusion", + "illusive", + "image", + "imaginary", + "imagines", + "imaging", + "imbecile", + "imitate", + "imitation", + "immature", + "immerse", + "immersion", + "imminent", + "immobile", + "immodest", + "immorally", + "immortal", + "immovable", + "immovably", + "immunity", + "immunize", + "impaired", + "impale", + "impart", + "impatient", + "impeach", + "impeding", + "impending", + "imperfect", + "imperial", + "impish", + "implant", + "implement", + "implicate", + "implicit", + "implode", + "implosion", + "implosive", + "imply", + "impolite", + "important", + "importer", + "impose", + "imposing", + "impotence", + "impotency", + "impotent", + "impound", + "imprecise", + "imprint", + "imprison", + "impromptu", + "improper", + "improve", + "improving", + "improvise", + "imprudent", + "impulse", + "impulsive", + "impure", + "impurity", + "iodine", + "iodize", + "ion", + "ipad", + "iphone", + "ipod", + "irate", + "irk", + "iron", + "irregular", + "irrigate", + "irritable", + "irritably", + "irritant", + "irritate", + "islamic", + "islamist", + "isolated", + "isolating", + "isolation", + "isotope", + "issue", + "issuing", + "italicize", + "italics", + "item", + "itinerary", + "itunes", + "ivory", + "ivy", + "jab", + "jackal", + "jacket", + "jackknife", + "jackpot", + "jailbird", + "jailbreak", + "jailer", + "jailhouse", + "jalapeno", + "jam", + "janitor", + "january", + "jargon", + "jarring", + "jasmine", + "jaundice", + "jaunt", + "java", + "jawed", + "jawless", + "jawline", + "jaws", + "jaybird", + "jaywalker", + "jazz", + "jeep", + "jeeringly", + "jellied", + "jelly", + "jersey", + "jester", + "jet", + "jiffy", + "jigsaw", + "jimmy", + "jingle", + "jingling", + "jinx", + "jitters", + "jittery", + "job", + "jockey", + "jockstrap", + "jogger", + "jogging", + "john", + "joining", + "jokester", + "jokingly", + "jolliness", + "jolly", + "jolt", + "jot", + "jovial", + "joyfully", + "joylessly", + "joyous", + "joyride", + "joystick", + "jubilance", + "jubilant", + "judge", + "judgingly", + "judicial", + "judiciary", + "judo", + "juggle", + "juggling", + "jugular", + "juice", + "juiciness", + "juicy", + "jujitsu", + "jukebox", + "july", + "jumble", + "jumbo", + "jump", + "junction", + "juncture", + "june", + "junior", + "juniper", + "junkie", + "junkman", + "junkyard", + "jurist", + "juror", + "jury", + "justice", + "justifier", + "justify", + "justly", + "justness", + "juvenile", + "kabob", + "kangaroo", + "karaoke", + "karate", + "karma", + "kebab", + "keenly", + "keenness", + "keep", + "keg", + "kelp", + "kennel", + "kept", + "kerchief", + "kerosene", + "kettle", + "kick", + "kiln", + "kilobyte", + "kilogram", + "kilometer", + "kilowatt", + "kilt", + "kimono", + "kindle", + "kindling", + "kindly", + "kindness", + "kindred", + "kinetic", + "kinfolk", + "king", + "kinship", + "kinsman", + "kinswoman", + "kissable", + "kisser", + "kissing", + "kitchen", + "kite", + "kitten", + "kitty", + "kiwi", + "kleenex", + "knapsack", + "knee", + "knelt", + "knickers", + "knoll", + "koala", + "kooky", + "kosher", + "krypton", + "kudos", + "kung", + "labored", + "laborer", + "laboring", + "laborious", + "labrador", + "ladder", + "ladies", + "ladle", + "ladybug", + "ladylike", + "lagged", + "lagging", + "lagoon", + "lair", + "lake", + "lance", + "landed", + "landfall", + "landfill", + "landing", + "landlady", + "landless", + "landline", + "landlord", + "landmark", + "landmass", + "landmine", + "landowner", + "landscape", + "landside", + "landslide", + "language", + "lankiness", + "lanky", + "lantern", + "lapdog", + "lapel", + "lapped", + "lapping", + "laptop", + "lard", + "large", + "lark", + "lash", + "lasso", + "last", + "latch", + "late", + "lather", + "latitude", + "latrine", + "latter", + "latticed", + "launch", + "launder", + "laundry", + "laurel", + "lavender", + "lavish", + "laxative", + "lazily", + "laziness", + "lazy", + "lecturer", + "left", + "legacy", + "legal", + "legend", + "legged", + "leggings", + "legible", + "legibly", + "legislate", + "lego", + "legroom", + "legume", + "legwarmer", + "legwork", + "lemon", + "lend", + "length", + "lens", + "lent", + "leotard", + "lesser", + "letdown", + "lethargic", + "lethargy", + "letter", + "lettuce", + "level", + "leverage", + "levers", + "levitate", + "levitator", + "liability", + "liable", + "liberty", + "librarian", + "library", + "licking", + "licorice", + "lid", + "life", + "lifter", + "lifting", + "liftoff", + "ligament", + "likely", + "likeness", + "likewise", + "liking", + "lilac", + "lilly", + "lily", + "limb", + "limeade", + "limelight", + "limes", + "limit", + "limping", + "limpness", + "line", + "lingo", + "linguini", + "linguist", + "lining", + "linked", + "linoleum", + "linseed", + "lint", + "lion", + "lip", + "liquefy", + "liqueur", + "liquid", + "lisp", + "list", + "litigate", + "litigator", + "litmus", + "litter", + "little", + "livable", + "lived", + "lively", + "liver", + "livestock", + "lividly", + "living", + "lizard", + "lubricant", + "lubricate", + "lucid", + "luckily", + "luckiness", + "luckless", + "lucrative", + "ludicrous", + "lugged", + "lukewarm", + "lullaby", + "lumber", + "luminance", + "luminous", + "lumpiness", + "lumping", + "lumpish", + "lunacy", + "lunar", + "lunchbox", + "luncheon", + "lunchroom", + "lunchtime", + "lung", + "lurch", + "lure", + "luridness", + "lurk", + "lushly", + "lushness", + "luster", + "lustfully", + "lustily", + "lustiness", + "lustrous", + "lusty", + "luxurious", + "luxury", + "lying", + "lyrically", + "lyricism", + "lyricist", + "lyrics", + "macarena", + "macaroni", + "macaw", + "mace", + "machine", + "machinist", + "magazine", + "magenta", + "maggot", + "magical", + "magician", + "magma", + "magnesium", + "magnetic", + "magnetism", + "magnetize", + "magnifier", + "magnify", + "magnitude", + "magnolia", + "mahogany", + "maimed", + "majestic", + "majesty", + "majorette", + "majority", + "makeover", + "maker", + "makeshift", + "making", + "malformed", + "malt", + "mama", + "mammal", + "mammary", + "mammogram", + "manager", + "managing", + "manatee", + "mandarin", + "mandate", + "mandatory", + "mandolin", + "manger", + "mangle", + "mango", + "mangy", + "manhandle", + "manhole", + "manhood", + "manhunt", + "manicotti", + "manicure", + "manifesto", + "manila", + "mankind", + "manlike", + "manliness", + "manly", + "manmade", + "manned", + "mannish", + "manor", + "manpower", + "mantis", + "mantra", + "manual", + "many", + "map", + "marathon", + "marauding", + "marbled", + "marbles", + "marbling", + "march", + "mardi", + "margarine", + "margarita", + "margin", + "marigold", + "marina", + "marine", + "marital", + "maritime", + "marlin", + "marmalade", + "maroon", + "married", + "marrow", + "marry", + "marshland", + "marshy", + "marsupial", + "marvelous", + "marxism", + "mascot", + "masculine", + "mashed", + "mashing", + "massager", + "masses", + "massive", + "mastiff", + "matador", + "matchbook", + "matchbox", + "matcher", + "matching", + "matchless", + "material", + "maternal", + "maternity", + "math", + "mating", + "matriarch", + "matrimony", + "matrix", + "matron", + "matted", + "matter", + "maturely", + "maturing", + "maturity", + "mauve", + "maverick", + "maximize", + "maximum", + "maybe", + "mayday", + "mayflower", + "moaner", + "moaning", + "mobile", + "mobility", + "mobilize", + "mobster", + "mocha", + "mocker", + "mockup", + "modified", + "modify", + "modular", + "modulator", + "module", + "moisten", + "moistness", + "moisture", + "molar", + "molasses", + "mold", + "molecular", + "molecule", + "molehill", + "mollusk", + "mom", + "monastery", + "monday", + "monetary", + "monetize", + "moneybags", + "moneyless", + "moneywise", + "mongoose", + "mongrel", + "monitor", + "monkhood", + "monogamy", + "monogram", + "monologue", + "monopoly", + "monorail", + "monotone", + "monotype", + "monoxide", + "monsieur", + "monsoon", + "monstrous", + "monthly", + "monument", + "moocher", + "moodiness", + "moody", + "mooing", + "moonbeam", + "mooned", + "moonlight", + "moonlike", + "moonlit", + "moonrise", + "moonscape", + "moonshine", + "moonstone", + "moonwalk", + "mop", + "morale", + "morality", + "morally", + "morbidity", + "morbidly", + "morphine", + "morphing", + "morse", + "mortality", + "mortally", + "mortician", + "mortified", + "mortify", + "mortuary", + "mosaic", + "mossy", + "most", + "mothball", + "mothproof", + "motion", + "motivate", + "motivator", + "motive", + "motocross", + "motor", + "motto", + "mountable", + "mountain", + "mounted", + "mounting", + "mourner", + "mournful", + "mouse", + "mousiness", + "moustache", + "mousy", + "mouth", + "movable", + "move", + "movie", + "moving", + "mower", + "mowing", + "much", + "muck", + "mud", + "mug", + "mulberry", + "mulch", + "mule", + "mulled", + "mullets", + "multiple", + "multiply", + "multitask", + "multitude", + "mumble", + "mumbling", + "mumbo", + "mummified", + "mummify", + "mummy", + "mumps", + "munchkin", + "mundane", + "municipal", + "muppet", + "mural", + "murkiness", + "murky", + "murmuring", + "muscular", + "museum", + "mushily", + "mushiness", + "mushroom", + "mushy", + "music", + "musket", + "muskiness", + "musky", + "mustang", + "mustard", + "muster", + "mustiness", + "musty", + "mutable", + "mutate", + "mutation", + "mute", + "mutilated", + "mutilator", + "mutiny", + "mutt", + "mutual", + "muzzle", + "myself", + "myspace", + "mystified", + "mystify", + "myth", + "nacho", + "nag", + "nail", + "name", + "naming", + "nanny", + "nanometer", + "nape", + "napkin", + "napped", + "napping", + "nappy", + "narrow", + "nastily", + "nastiness", + "national", + "native", + "nativity", + "natural", + "nature", + "naturist", + "nautical", + "navigate", + "navigator", + "navy", + "nearby", + "nearest", + "nearly", + "nearness", + "neatly", + "neatness", + "nebula", + "nebulizer", + "nectar", + "negate", + "negation", + "negative", + "neglector", + "negligee", + "negligent", + "negotiate", + "nemeses", + "nemesis", + "neon", + "nephew", + "nerd", + "nervous", + "nervy", + "nest", + "net", + "neurology", + "neuron", + "neurosis", + "neurotic", + "neuter", + "neutron", + "never", + "next", + "nibble", + "nickname", + "nicotine", + "niece", + "nifty", + "nimble", + "nimbly", + "nineteen", + "ninetieth", + "ninja", + "nintendo", + "ninth", + "nuclear", + "nuclei", + "nucleus", + "nugget", + "nullify", + "number", + "numbing", + "numbly", + "numbness", + "numeral", + "numerate", + "numerator", + "numeric", + "numerous", + "nuptials", + "nursery", + "nursing", + "nurture", + "nutcase", + "nutlike", + "nutmeg", + "nutrient", + "nutshell", + "nuttiness", + "nutty", + "nuzzle", + "nylon", + "oaf", + "oak", + "oasis", + "oat", + "obedience", + "obedient", + "obituary", + "object", + "obligate", + "obliged", + "oblivion", + "oblivious", + "oblong", + "obnoxious", + "oboe", + "obscure", + "obscurity", + "observant", + "observer", + "observing", + "obsessed", + "obsession", + "obsessive", + "obsolete", + "obstacle", + "obstinate", + "obstruct", + "obtain", + "obtrusive", + "obtuse", + "obvious", + "occultist", + "occupancy", + "occupant", + "occupier", + "occupy", + "ocean", + "ocelot", + "octagon", + "octane", + "october", + "octopus", + "ogle", + "oil", + "oink", + "ointment", + "okay", + "old", + "olive", + "olympics", + "omega", + "omen", + "ominous", + "omission", + "omit", + "omnivore", + "onboard", + "oncoming", + "ongoing", + "onion", + "online", + "onlooker", + "only", + "onscreen", + "onset", + "onshore", + "onslaught", + "onstage", + "onto", + "onward", + "onyx", + "oops", + "ooze", + "oozy", + "opacity", + "opal", + "open", + "operable", + "operate", + "operating", + "operation", + "operative", + "operator", + "opium", + "opossum", + "opponent", + "oppose", + "opposing", + "opposite", + "oppressed", + "oppressor", + "opt", + "opulently", + "osmosis", + "other", + "otter", + "ouch", + "ought", + "ounce", + "outage", + "outback", + "outbid", + "outboard", + "outbound", + "outbreak", + "outburst", + "outcast", + "outclass", + "outcome", + "outdated", + "outdoors", + "outer", + "outfield", + "outfit", + "outflank", + "outgoing", + "outgrow", + "outhouse", + "outing", + "outlast", + "outlet", + "outline", + "outlook", + "outlying", + "outmatch", + "outmost", + "outnumber", + "outplayed", + "outpost", + "outpour", + "output", + "outrage", + "outrank", + "outreach", + "outright", + "outscore", + "outsell", + "outshine", + "outshoot", + "outsider", + "outskirts", + "outsmart", + "outsource", + "outspoken", + "outtakes", + "outthink", + "outward", + "outweigh", + "outwit", + "oval", + "ovary", + "oven", + "overact", + "overall", + "overarch", + "overbid", + "overbill", + "overbite", + "overblown", + "overboard", + "overbook", + "overbuilt", + "overcast", + "overcoat", + "overcome", + "overcook", + "overcrowd", + "overdraft", + "overdrawn", + "overdress", + "overdrive", + "overdue", + "overeager", + "overeater", + "overexert", + "overfed", + "overfeed", + "overfill", + "overflow", + "overfull", + "overgrown", + "overhand", + "overhang", + "overhaul", + "overhead", + "overhear", + "overheat", + "overhung", + "overjoyed", + "overkill", + "overlabor", + "overlaid", + "overlap", + "overlay", + "overload", + "overlook", + "overlord", + "overlying", + "overnight", + "overpass", + "overpay", + "overplant", + "overplay", + "overpower", + "overprice", + "overrate", + "overreach", + "overreact", + "override", + "overripe", + "overrule", + "overrun", + "overshoot", + "overshot", + "oversight", + "oversized", + "oversleep", + "oversold", + "overspend", + "overstate", + "overstay", + "overstep", + "overstock", + "overstuff", + "oversweet", + "overtake", + "overthrow", + "overtime", + "overtly", + "overtone", + "overture", + "overturn", + "overuse", + "overvalue", + "overview", + "overwrite", + "owl", + "oxford", + "oxidant", + "oxidation", + "oxidize", + "oxidizing", + "oxygen", + "oxymoron", + "oyster", + "ozone", + "paced", + "pacemaker", + "pacific", + "pacifier", + "pacifism", + "pacifist", + "pacify", + "padded", + "padding", + "paddle", + "paddling", + "padlock", + "pagan", + "pager", + "paging", + "pajamas", + "palace", + "palatable", + "palm", + "palpable", + "palpitate", + "paltry", + "pampered", + "pamperer", + "pampers", + "pamphlet", + "panama", + "pancake", + "pancreas", + "panda", + "pandemic", + "pang", + "panhandle", + "panic", + "panning", + "panorama", + "panoramic", + "panther", + "pantomime", + "pantry", + "pants", + "pantyhose", + "paparazzi", + "papaya", + "paper", + "paprika", + "papyrus", + "parabola", + "parachute", + "parade", + "paradox", + "paragraph", + "parakeet", + "paralegal", + "paralyses", + "paralysis", + "paralyze", + "paramedic", + "parameter", + "paramount", + "parasail", + "parasite", + "parasitic", + "parcel", + "parched", + "parchment", + "pardon", + "parish", + "parka", + "parking", + "parkway", + "parlor", + "parmesan", + "parole", + "parrot", + "parsley", + "parsnip", + "partake", + "parted", + "parting", + "partition", + "partly", + "partner", + "partridge", + "party", + "passable", + "passably", + "passage", + "passcode", + "passenger", + "passerby", + "passing", + "passion", + "passive", + "passivism", + "passover", + "passport", + "password", + "pasta", + "pasted", + "pastel", + "pastime", + "pastor", + "pastrami", + "pasture", + "pasty", + "patchwork", + "patchy", + "paternal", + "paternity", + "path", + "patience", + "patient", + "patio", + "patriarch", + "patriot", + "patrol", + "patronage", + "patronize", + "pauper", + "pavement", + "paver", + "pavestone", + "pavilion", + "paving", + "pawing", + "payable", + "payback", + "paycheck", + "payday", + "payee", + "payer", + "paying", + "payment", + "payphone", + "payroll", + "pebble", + "pebbly", + "pecan", + "pectin", + "peculiar", + "peddling", + "pediatric", + "pedicure", + "pedigree", + "pedometer", + "pegboard", + "pelican", + "pellet", + "pelt", + "pelvis", + "penalize", + "penalty", + "pencil", + "pendant", + "pending", + "penholder", + "penknife", + "pennant", + "penniless", + "penny", + "penpal", + "pension", + "pentagon", + "pentagram", + "pep", + "perceive", + "percent", + "perch", + "percolate", + "perennial", + "perfected", + "perfectly", + "perfume", + "periscope", + "perish", + "perjurer", + "perjury", + "perkiness", + "perky", + "perm", + "peroxide", + "perpetual", + "perplexed", + "persecute", + "persevere", + "persuaded", + "persuader", + "pesky", + "peso", + "pessimism", + "pessimist", + "pester", + "pesticide", + "petal", + "petite", + "petition", + "petri", + "petroleum", + "petted", + "petticoat", + "pettiness", + "petty", + "petunia", + "phantom", + "phobia", + "phoenix", + "phonebook", + "phoney", + "phonics", + "phoniness", + "phony", + "phosphate", + "photo", + "phrase", + "phrasing", + "placard", + "placate", + "placidly", + "plank", + "planner", + "plant", + "plasma", + "plaster", + "plastic", + "plated", + "platform", + "plating", + "platinum", + "platonic", + "platter", + "platypus", + "plausible", + "plausibly", + "playable", + "playback", + "player", + "playful", + "playgroup", + "playhouse", + "playing", + "playlist", + "playmaker", + "playmate", + "playoff", + "playpen", + "playroom", + "playset", + "plaything", + "playtime", + "plaza", + "pleading", + "pleat", + "pledge", + "plentiful", + "plenty", + "plethora", + "plexiglas", + "pliable", + "plod", + "plop", + "plot", + "plow", + "ploy", + "pluck", + "plug", + "plunder", + "plunging", + "plural", + "plus", + "plutonium", + "plywood", + "poach", + "pod", + "poem", + "poet", + "pogo", + "pointed", + "pointer", + "pointing", + "pointless", + "pointy", + "poise", + "poison", + "poker", + "poking", + "polar", + "police", + "policy", + "polio", + "polish", + "politely", + "polka", + "polo", + "polyester", + "polygon", + "polygraph", + "polymer", + "poncho", + "pond", + "pony", + "popcorn", + "pope", + "poplar", + "popper", + "poppy", + "popsicle", + "populace", + "popular", + "populate", + "porcupine", + "pork", + "porous", + "porridge", + "portable", + "portal", + "portfolio", + "porthole", + "portion", + "portly", + "portside", + "poser", + "posh", + "posing", + "possible", + "possibly", + "possum", + "postage", + "postal", + "postbox", + "postcard", + "posted", + "poster", + "posting", + "postnasal", + "posture", + "postwar", + "pouch", + "pounce", + "pouncing", + "pound", + "pouring", + "pout", + "powdered", + "powdering", + "powdery", + "power", + "powwow", + "pox", + "praising", + "prance", + "prancing", + "pranker", + "prankish", + "prankster", + "prayer", + "praying", + "preacher", + "preaching", + "preachy", + "preamble", + "precinct", + "precise", + "precision", + "precook", + "precut", + "predator", + "predefine", + "predict", + "preface", + "prefix", + "preflight", + "preformed", + "pregame", + "pregnancy", + "pregnant", + "preheated", + "prelaunch", + "prelaw", + "prelude", + "premiere", + "premises", + "premium", + "prenatal", + "preoccupy", + "preorder", + "prepaid", + "prepay", + "preplan", + "preppy", + "preschool", + "prescribe", + "preseason", + "preset", + "preshow", + "president", + "presoak", + "press", + "presume", + "presuming", + "preteen", + "pretended", + "pretender", + "pretense", + "pretext", + "pretty", + "pretzel", + "prevail", + "prevalent", + "prevent", + "preview", + "previous", + "prewar", + "prewashed", + "prideful", + "pried", + "primal", + "primarily", + "primary", + "primate", + "primer", + "primp", + "princess", + "print", + "prior", + "prism", + "prison", + "prissy", + "pristine", + "privacy", + "private", + "privatize", + "prize", + "proactive", + "probable", + "probably", + "probation", + "probe", + "probing", + "probiotic", + "problem", + "procedure", + "process", + "proclaim", + "procreate", + "procurer", + "prodigal", + "prodigy", + "produce", + "product", + "profane", + "profanity", + "professed", + "professor", + "profile", + "profound", + "profusely", + "progeny", + "prognosis", + "program", + "progress", + "projector", + "prologue", + "prolonged", + "promenade", + "prominent", + "promoter", + "promotion", + "prompter", + "promptly", + "prone", + "prong", + "pronounce", + "pronto", + "proofing", + "proofread", + "proofs", + "propeller", + "properly", + "property", + "proponent", + "proposal", + "propose", + "props", + "prorate", + "protector", + "protegee", + "proton", + "prototype", + "protozoan", + "protract", + "protrude", + "proud", + "provable", + "proved", + "proven", + "provided", + "provider", + "providing", + "province", + "proving", + "provoke", + "provoking", + "provolone", + "prowess", + "prowler", + "prowling", + "proximity", + "proxy", + "prozac", + "prude", + "prudishly", + "prune", + "pruning", + "pry", + "psychic", + "public", + "publisher", + "pucker", + "pueblo", + "pug", + "pull", + "pulmonary", + "pulp", + "pulsate", + "pulse", + "pulverize", + "puma", + "pumice", + "pummel", + "punch", + "punctual", + "punctuate", + "punctured", + "pungent", + "punisher", + "punk", + "pupil", + "puppet", + "puppy", + "purchase", + "pureblood", + "purebred", + "purely", + "pureness", + "purgatory", + "purge", + "purging", + "purifier", + "purify", + "purist", + "puritan", + "purity", + "purple", + "purplish", + "purposely", + "purr", + "purse", + "pursuable", + "pursuant", + "pursuit", + "purveyor", + "pushcart", + "pushchair", + "pusher", + "pushiness", + "pushing", + "pushover", + "pushpin", + "pushup", + "pushy", + "putdown", + "putt", + "puzzle", + "puzzling", + "pyramid", + "pyromania", + "python", + "quack", + "quadrant", + "quail", + "quaintly", + "quake", + "quaking", + "qualified", + "qualifier", + "qualify", + "quality", + "qualm", + "quantum", + "quarrel", + "quarry", + "quartered", + "quarterly", + "quarters", + "quartet", + "quench", + "query", + "quicken", + "quickly", + "quickness", + "quicksand", + "quickstep", + "quiet", + "quill", + "quilt", + "quintet", + "quintuple", + "quirk", + "quit", + "quiver", + "quizzical", + "quotable", + "quotation", + "quote", + "rabid", + "race", + "racing", + "racism", + "rack", + "racoon", + "radar", + "radial", + "radiance", + "radiantly", + "radiated", + "radiation", + "radiator", + "radio", + "radish", + "raffle", + "raft", + "rage", + "ragged", + "raging", + "ragweed", + "raider", + "railcar", + "railing", + "railroad", + "railway", + "raisin", + "rake", + "raking", + "rally", + "ramble", + "rambling", + "ramp", + "ramrod", + "ranch", + "rancidity", + "random", + "ranged", + "ranger", + "ranging", + "ranked", + "ranking", + "ransack", + "ranting", + "rants", + "rare", + "rarity", + "rascal", + "rash", + "rasping", + "ravage", + "raven", + "ravine", + "raving", + "ravioli", + "ravishing", + "reabsorb", + "reach", + "reacquire", + "reaction", + "reactive", + "reactor", + "reaffirm", + "ream", + "reanalyze", + "reappear", + "reapply", + "reappoint", + "reapprove", + "rearrange", + "rearview", + "reason", + "reassign", + "reassure", + "reattach", + "reawake", + "rebalance", + "rebate", + "rebel", + "rebirth", + "reboot", + "reborn", + "rebound", + "rebuff", + "rebuild", + "rebuilt", + "reburial", + "rebuttal", + "recall", + "recant", + "recapture", + "recast", + "recede", + "recent", + "recess", + "recharger", + "recipient", + "recital", + "recite", + "reckless", + "reclaim", + "recliner", + "reclining", + "recluse", + "reclusive", + "recognize", + "recoil", + "recollect", + "recolor", + "reconcile", + "reconfirm", + "reconvene", + "recopy", + "record", + "recount", + "recoup", + "recovery", + "recreate", + "rectal", + "rectangle", + "rectified", + "rectify", + "recycled", + "recycler", + "recycling", + "reemerge", + "reenact", + "reenter", + "reentry", + "reexamine", + "referable", + "referee", + "reference", + "refill", + "refinance", + "refined", + "refinery", + "refining", + "refinish", + "reflected", + "reflector", + "reflex", + "reflux", + "refocus", + "refold", + "reforest", + "reformat", + "reformed", + "reformer", + "reformist", + "refract", + "refrain", + "refreeze", + "refresh", + "refried", + "refueling", + "refund", + "refurbish", + "refurnish", + "refusal", + "refuse", + "refusing", + "refutable", + "refute", + "regain", + "regalia", + "regally", + "reggae", + "regime", + "region", + "register", + "registrar", + "registry", + "regress", + "regretful", + "regroup", + "regular", + "regulate", + "regulator", + "rehab", + "reheat", + "rehire", + "rehydrate", + "reimburse", + "reissue", + "reiterate", + "rejoice", + "rejoicing", + "rejoin", + "rekindle", + "relapse", + "relapsing", + "relatable", + "related", + "relation", + "relative", + "relax", + "relay", + "relearn", + "release", + "relenting", + "reliable", + "reliably", + "reliance", + "reliant", + "relic", + "relieve", + "relieving", + "relight", + "relish", + "relive", + "reload", + "relocate", + "relock", + "reluctant", + "rely", + "remake", + "remark", + "remarry", + "rematch", + "remedial", + "remedy", + "remember", + "reminder", + "remindful", + "remission", + "remix", + "remnant", + "remodeler", + "remold", + "remorse", + "remote", + "removable", + "removal", + "removed", + "remover", + "removing", + "rename", + "renderer", + "rendering", + "rendition", + "renegade", + "renewable", + "renewably", + "renewal", + "renewed", + "renounce", + "renovate", + "renovator", + "rentable", + "rental", + "rented", + "renter", + "reoccupy", + "reoccur", + "reopen", + "reorder", + "repackage", + "repacking", + "repaint", + "repair", + "repave", + "repaying", + "repayment", + "repeal", + "repeated", + "repeater", + "repent", + "rephrase", + "replace", + "replay", + "replica", + "reply", + "reporter", + "repose", + "repossess", + "repost", + "repressed", + "reprimand", + "reprint", + "reprise", + "reproach", + "reprocess", + "reproduce", + "reprogram", + "reps", + "reptile", + "reptilian", + "repugnant", + "repulsion", + "repulsive", + "repurpose", + "reputable", + "reputably", + "request", + "require", + "requisite", + "reroute", + "rerun", + "resale", + "resample", + "rescuer", + "reseal", + "research", + "reselect", + "reseller", + "resemble", + "resend", + "resent", + "reset", + "reshape", + "reshoot", + "reshuffle", + "residence", + "residency", + "resident", + "residual", + "residue", + "resigned", + "resilient", + "resistant", + "resisting", + "resize", + "resolute", + "resolved", + "resonant", + "resonate", + "resort", + "resource", + "respect", + "resubmit", + "result", + "resume", + "resupply", + "resurface", + "resurrect", + "retail", + "retainer", + "retaining", + "retake", + "retaliate", + "retention", + "rethink", + "retinal", + "retired", + "retiree", + "retiring", + "retold", + "retool", + "retorted", + "retouch", + "retrace", + "retract", + "retrain", + "retread", + "retreat", + "retrial", + "retrieval", + "retriever", + "retry", + "return", + "retying", + "retype", + "reunion", + "reunite", + "reusable", + "reuse", + "reveal", + "reveler", + "revenge", + "revenue", + "reverb", + "revered", + "reverence", + "reverend", + "reversal", + "reverse", + "reversing", + "reversion", + "revert", + "revisable", + "revise", + "revision", + "revisit", + "revivable", + "revival", + "reviver", + "reviving", + "revocable", + "revoke", + "revolt", + "revolver", + "revolving", + "reward", + "rewash", + "rewind", + "rewire", + "reword", + "rework", + "rewrap", + "rewrite", + "rhyme", + "ribbon", + "ribcage", + "rice", + "riches", + "richly", + "richness", + "rickety", + "ricotta", + "riddance", + "ridden", + "ride", + "riding", + "rifling", + "rift", + "rigging", + "rigid", + "rigor", + "rimless", + "rimmed", + "rind", + "rink", + "rinse", + "rinsing", + "riot", + "ripcord", + "ripeness", + "ripening", + "ripping", + "ripple", + "rippling", + "riptide", + "rise", + "rising", + "risk", + "risotto", + "ritalin", + "ritzy", + "rival", + "riverbank", + "riverbed", + "riverboat", + "riverside", + "riveter", + "riveting", + "roamer", + "roaming", + "roast", + "robbing", + "robe", + "robin", + "robotics", + "robust", + "rockband", + "rocker", + "rocket", + "rockfish", + "rockiness", + "rocking", + "rocklike", + "rockslide", + "rockstar", + "rocky", + "rogue", + "roman", + "romp", + "rope", + "roping", + "roster", + "rosy", + "rotten", + "rotting", + "rotunda", + "roulette", + "rounding", + "roundish", + "roundness", + "roundup", + "roundworm", + "routine", + "routing", + "rover", + "roving", + "royal", + "rubbed", + "rubber", + "rubbing", + "rubble", + "rubdown", + "ruby", + "ruckus", + "rudder", + "rug", + "ruined", + "rule", + "rumble", + "rumbling", + "rummage", + "rumor", + "runaround", + "rundown", + "runner", + "running", + "runny", + "runt", + "runway", + "rupture", + "rural", + "ruse", + "rush", + "rust", + "rut", + "sabbath", + "sabotage", + "sacrament", + "sacred", + "sacrifice", + "sadden", + "saddlebag", + "saddled", + "saddling", + "sadly", + "sadness", + "safari", + "safeguard", + "safehouse", + "safely", + "safeness", + "saffron", + "saga", + "sage", + "sagging", + "saggy", + "said", + "saint", + "sake", + "salad", + "salami", + "salaried", + "salary", + "saline", + "salon", + "saloon", + "salsa", + "salt", + "salutary", + "salute", + "salvage", + "salvaging", + "salvation", + "same", + "sample", + "sampling", + "sanction", + "sanctity", + "sanctuary", + "sandal", + "sandbag", + "sandbank", + "sandbar", + "sandblast", + "sandbox", + "sanded", + "sandfish", + "sanding", + "sandlot", + "sandpaper", + "sandpit", + "sandstone", + "sandstorm", + "sandworm", + "sandy", + "sanitary", + "sanitizer", + "sank", + "santa", + "sapling", + "sappiness", + "sappy", + "sarcasm", + "sarcastic", + "sardine", + "sash", + "sasquatch", + "sassy", + "satchel", + "satiable", + "satin", + "satirical", + "satisfied", + "satisfy", + "saturate", + "saturday", + "sauciness", + "saucy", + "sauna", + "savage", + "savanna", + "saved", + "savings", + "savior", + "savor", + "saxophone", + "say", + "scabbed", + "scabby", + "scalded", + "scalding", + "scale", + "scaling", + "scallion", + "scallop", + "scalping", + "scam", + "scandal", + "scanner", + "scanning", + "scant", + "scapegoat", + "scarce", + "scarcity", + "scarecrow", + "scared", + "scarf", + "scarily", + "scariness", + "scarring", + "scary", + "scavenger", + "scenic", + "schedule", + "schematic", + "scheme", + "scheming", + "schilling", + "schnapps", + "scholar", + "science", + "scientist", + "scion", + "scoff", + "scolding", + "scone", + "scoop", + "scooter", + "scope", + "scorch", + "scorebook", + "scorecard", + "scored", + "scoreless", + "scorer", + "scoring", + "scorn", + "scorpion", + "scotch", + "scoundrel", + "scoured", + "scouring", + "scouting", + "scouts", + "scowling", + "scrabble", + "scraggly", + "scrambled", + "scrambler", + "scrap", + "scratch", + "scrawny", + "screen", + "scribble", + "scribe", + "scribing", + "scrimmage", + "script", + "scroll", + "scrooge", + "scrounger", + "scrubbed", + "scrubber", + "scruffy", + "scrunch", + "scrutiny", + "scuba", + "scuff", + "sculptor", + "sculpture", + "scurvy", + "scuttle", + "secluded", + "secluding", + "seclusion", + "second", + "secrecy", + "secret", + "sectional", + "sector", + "secular", + "securely", + "security", + "sedan", + "sedate", + "sedation", + "sedative", + "sediment", + "seduce", + "seducing", + "segment", + "seismic", + "seizing", + "seldom", + "selected", + "selection", + "selective", + "selector", + "self", + "seltzer", + "semantic", + "semester", + "semicolon", + "semifinal", + "seminar", + "semisoft", + "semisweet", + "senate", + "senator", + "send", + "senior", + "senorita", + "sensation", + "sensitive", + "sensitize", + "sensually", + "sensuous", + "sepia", + "september", + "septic", + "septum", + "sequel", + "sequence", + "sequester", + "series", + "sermon", + "serotonin", + "serpent", + "serrated", + "serve", + "service", + "serving", + "sesame", + "sessions", + "setback", + "setting", + "settle", + "settling", + "setup", + "sevenfold", + "seventeen", + "seventh", + "seventy", + "severity", + "shabby", + "shack", + "shaded", + "shadily", + "shadiness", + "shading", + "shadow", + "shady", + "shaft", + "shakable", + "shakily", + "shakiness", + "shaking", + "shaky", + "shale", + "shallot", + "shallow", + "shame", + "shampoo", + "shamrock", + "shank", + "shanty", + "shape", + "shaping", + "share", + "sharpener", + "sharper", + "sharpie", + "sharply", + "sharpness", + "shawl", + "sheath", + "shed", + "sheep", + "sheet", + "shelf", + "shell", + "shelter", + "shelve", + "shelving", + "sherry", + "shield", + "shifter", + "shifting", + "shiftless", + "shifty", + "shimmer", + "shimmy", + "shindig", + "shine", + "shingle", + "shininess", + "shining", + "shiny", + "ship", + "shirt", + "shivering", + "shock", + "shone", + "shoplift", + "shopper", + "shopping", + "shoptalk", + "shore", + "shortage", + "shortcake", + "shortcut", + "shorten", + "shorter", + "shorthand", + "shortlist", + "shortly", + "shortness", + "shorts", + "shortwave", + "shorty", + "shout", + "shove", + "showbiz", + "showcase", + "showdown", + "shower", + "showgirl", + "showing", + "showman", + "shown", + "showoff", + "showpiece", + "showplace", + "showroom", + "showy", + "shrank", + "shrapnel", + "shredder", + "shredding", + "shrewdly", + "shriek", + "shrill", + "shrimp", + "shrine", + "shrink", + "shrivel", + "shrouded", + "shrubbery", + "shrubs", + "shrug", + "shrunk", + "shucking", + "shudder", + "shuffle", + "shuffling", + "shun", + "shush", + "shut", + "shy", + "siamese", + "siberian", + "sibling", + "siding", + "sierra", + "siesta", + "sift", + "sighing", + "silenced", + "silencer", + "silent", + "silica", + "silicon", + "silk", + "silliness", + "silly", + "silo", + "silt", + "silver", + "similarly", + "simile", + "simmering", + "simple", + "simplify", + "simply", + "sincere", + "sincerity", + "singer", + "singing", + "single", + "singular", + "sinister", + "sinless", + "sinner", + "sinuous", + "sip", + "siren", + "sister", + "sitcom", + "sitter", + "sitting", + "situated", + "situation", + "sixfold", + "sixteen", + "sixth", + "sixties", + "sixtieth", + "sixtyfold", + "sizable", + "sizably", + "size", + "sizing", + "sizzle", + "sizzling", + "skater", + "skating", + "skedaddle", + "skeletal", + "skeleton", + "skeptic", + "sketch", + "skewed", + "skewer", + "skid", + "skied", + "skier", + "skies", + "skiing", + "skilled", + "skillet", + "skillful", + "skimmed", + "skimmer", + "skimming", + "skimpily", + "skincare", + "skinhead", + "skinless", + "skinning", + "skinny", + "skintight", + "skipper", + "skipping", + "skirmish", + "skirt", + "skittle", + "skydiver", + "skylight", + "skyline", + "skype", + "skyrocket", + "skyward", + "slab", + "slacked", + "slacker", + "slacking", + "slackness", + "slacks", + "slain", + "slam", + "slander", + "slang", + "slapping", + "slapstick", + "slashed", + "slashing", + "slate", + "slather", + "slaw", + "sled", + "sleek", + "sleep", + "sleet", + "sleeve", + "slept", + "sliceable", + "sliced", + "slicer", + "slicing", + "slick", + "slider", + "slideshow", + "sliding", + "slighted", + "slighting", + "slightly", + "slimness", + "slimy", + "slinging", + "slingshot", + "slinky", + "slip", + "slit", + "sliver", + "slobbery", + "slogan", + "sloped", + "sloping", + "sloppily", + "sloppy", + "slot", + "slouching", + "slouchy", + "sludge", + "slug", + "slum", + "slurp", + "slush", + "sly", + "small", + "smartly", + "smartness", + "smasher", + "smashing", + "smashup", + "smell", + "smelting", + "smile", + "smilingly", + "smirk", + "smite", + "smith", + "smitten", + "smock", + "smog", + "smoked", + "smokeless", + "smokiness", + "smoking", + "smoky", + "smolder", + "smooth", + "smother", + "smudge", + "smudgy", + "smuggler", + "smuggling", + "smugly", + "smugness", + "snack", + "snagged", + "snaking", + "snap", + "snare", + "snarl", + "snazzy", + "sneak", + "sneer", + "sneeze", + "sneezing", + "snide", + "sniff", + "snippet", + "snipping", + "snitch", + "snooper", + "snooze", + "snore", + "snoring", + "snorkel", + "snort", + "snout", + "snowbird", + "snowboard", + "snowbound", + "snowcap", + "snowdrift", + "snowdrop", + "snowfall", + "snowfield", + "snowflake", + "snowiness", + "snowless", + "snowman", + "snowplow", + "snowshoe", + "snowstorm", + "snowsuit", + "snowy", + "snub", + "snuff", + "snuggle", + "snugly", + "snugness", + "speak", + "spearfish", + "spearhead", + "spearman", + "spearmint", + "species", + "specimen", + "specked", + "speckled", + "specks", + "spectacle", + "spectator", + "spectrum", + "speculate", + "speech", + "speed", + "spellbind", + "speller", + "spelling", + "spendable", + "spender", + "spending", + "spent", + "spew", + "sphere", + "spherical", + "sphinx", + "spider", + "spied", + "spiffy", + "spill", + "spilt", + "spinach", + "spinal", + "spindle", + "spinner", + "spinning", + "spinout", + "spinster", + "spiny", + "spiral", + "spirited", + "spiritism", + "spirits", + "spiritual", + "splashed", + "splashing", + "splashy", + "splatter", + "spleen", + "splendid", + "splendor", + "splice", + "splicing", + "splinter", + "splotchy", + "splurge", + "spoilage", + "spoiled", + "spoiler", + "spoiling", + "spoils", + "spoken", + "spokesman", + "sponge", + "spongy", + "sponsor", + "spoof", + "spookily", + "spooky", + "spool", + "spoon", + "spore", + "sporting", + "sports", + "sporty", + "spotless", + "spotlight", + "spotted", + "spotter", + "spotting", + "spotty", + "spousal", + "spouse", + "spout", + "sprain", + "sprang", + "sprawl", + "spray", + "spree", + "sprig", + "spring", + "sprinkled", + "sprinkler", + "sprint", + "sprite", + "sprout", + "spruce", + "sprung", + "spry", + "spud", + "spur", + "sputter", + "spyglass", + "squabble", + "squad", + "squall", + "squander", + "squash", + "squatted", + "squatter", + "squatting", + "squeak", + "squealer", + "squealing", + "squeamish", + "squeegee", + "squeeze", + "squeezing", + "squid", + "squiggle", + "squiggly", + "squint", + "squire", + "squirt", + "squishier", + "squishy", + "stability", + "stabilize", + "stable", + "stack", + "stadium", + "staff", + "stage", + "staging", + "stagnant", + "stagnate", + "stainable", + "stained", + "staining", + "stainless", + "stalemate", + "staleness", + "stalling", + "stallion", + "stamina", + "stammer", + "stamp", + "stand", + "stank", + "staple", + "stapling", + "starboard", + "starch", + "stardom", + "stardust", + "starfish", + "stargazer", + "staring", + "stark", + "starless", + "starlet", + "starlight", + "starlit", + "starring", + "starry", + "starship", + "starter", + "starting", + "startle", + "startling", + "startup", + "starved", + "starving", + "stash", + "state", + "static", + "statistic", + "statue", + "stature", + "status", + "statute", + "statutory", + "staunch", + "stays", + "steadfast", + "steadier", + "steadily", + "steadying", + "steam", + "steed", + "steep", + "steerable", + "steering", + "steersman", + "stegosaur", + "stellar", + "stem", + "stench", + "stencil", + "step", + "stereo", + "sterile", + "sterility", + "sterilize", + "sterling", + "sternness", + "sternum", + "stew", + "stick", + "stiffen", + "stiffly", + "stiffness", + "stifle", + "stifling", + "stillness", + "stilt", + "stimulant", + "stimulate", + "stimuli", + "stimulus", + "stinger", + "stingily", + "stinging", + "stingray", + "stingy", + "stinking", + "stinky", + "stipend", + "stipulate", + "stir", + "stitch", + "stock", + "stoic", + "stoke", + "stole", + "stomp", + "stonewall", + "stoneware", + "stonework", + "stoning", + "stony", + "stood", + "stooge", + "stool", + "stoop", + "stoplight", + "stoppable", + "stoppage", + "stopped", + "stopper", + "stopping", + "stopwatch", + "storable", + "storage", + "storeroom", + "storewide", + "storm", + "stout", + "stove", + "stowaway", + "stowing", + "straddle", + "straggler", + "strained", + "strainer", + "straining", + "strangely", + "stranger", + "strangle", + "strategic", + "strategy", + "stratus", + "straw", + "stray", + "streak", + "stream", + "street", + "strength", + "strenuous", + "strep", + "stress", + "stretch", + "strewn", + "stricken", + "strict", + "stride", + "strife", + "strike", + "striking", + "strive", + "striving", + "strobe", + "strode", + "stroller", + "strongbox", + "strongly", + "strongman", + "struck", + "structure", + "strudel", + "struggle", + "strum", + "strung", + "strut", + "stubbed", + "stubble", + "stubbly", + "stubborn", + "stucco", + "stuck", + "student", + "studied", + "studio", + "study", + "stuffed", + "stuffing", + "stuffy", + "stumble", + "stumbling", + "stump", + "stung", + "stunned", + "stunner", + "stunning", + "stunt", + "stupor", + "sturdily", + "sturdy", + "styling", + "stylishly", + "stylist", + "stylized", + "stylus", + "suave", + "subarctic", + "subatomic", + "subdivide", + "subdued", + "subduing", + "subfloor", + "subgroup", + "subheader", + "subject", + "sublease", + "sublet", + "sublevel", + "sublime", + "submarine", + "submerge", + "submersed", + "submitter", + "subpanel", + "subpar", + "subplot", + "subprime", + "subscribe", + "subscript", + "subsector", + "subside", + "subsiding", + "subsidize", + "subsidy", + "subsoil", + "subsonic", + "substance", + "subsystem", + "subtext", + "subtitle", + "subtly", + "subtotal", + "subtract", + "subtype", + "suburb", + "subway", + "subwoofer", + "subzero", + "succulent", + "such", + "suction", + "sudden", + "sudoku", + "suds", + "sufferer", + "suffering", + "suffice", + "suffix", + "suffocate", + "suffrage", + "sugar", + "suggest", + "suing", + "suitable", + "suitably", + "suitcase", + "suitor", + "sulfate", + "sulfide", + "sulfite", + "sulfur", + "sulk", + "sullen", + "sulphate", + "sulphuric", + "sultry", + "superbowl", + "superglue", + "superhero", + "superior", + "superjet", + "superman", + "supermom", + "supernova", + "supervise", + "supper", + "supplier", + "supply", + "support", + "supremacy", + "supreme", + "surcharge", + "surely", + "sureness", + "surface", + "surfacing", + "surfboard", + "surfer", + "surgery", + "surgical", + "surging", + "surname", + "surpass", + "surplus", + "surprise", + "surreal", + "surrender", + "surrogate", + "surround", + "survey", + "survival", + "survive", + "surviving", + "survivor", + "sushi", + "suspect", + "suspend", + "suspense", + "sustained", + "sustainer", + "swab", + "swaddling", + "swagger", + "swampland", + "swan", + "swapping", + "swarm", + "sway", + "swear", + "sweat", + "sweep", + "swell", + "swept", + "swerve", + "swifter", + "swiftly", + "swiftness", + "swimmable", + "swimmer", + "swimming", + "swimsuit", + "swimwear", + "swinger", + "swinging", + "swipe", + "swirl", + "switch", + "swivel", + "swizzle", + "swooned", + "swoop", + "swoosh", + "swore", + "sworn", + "swung", + "sycamore", + "sympathy", + "symphonic", + "symphony", + "symptom", + "synapse", + "syndrome", + "synergy", + "synopses", + "synopsis", + "synthesis", + "synthetic", + "syrup", + "system", + "t-shirt", + "tabasco", + "tabby", + "tableful", + "tables", + "tablet", + "tableware", + "tabloid", + "tackiness", + "tacking", + "tackle", + "tackling", + "tacky", + "taco", + "tactful", + "tactical", + "tactics", + "tactile", + "tactless", + "tadpole", + "taekwondo", + "tag", + "tainted", + "take", + "taking", + "talcum", + "talisman", + "tall", + "talon", + "tamale", + "tameness", + "tamer", + "tamper", + "tank", + "tanned", + "tannery", + "tanning", + "tantrum", + "tapeless", + "tapered", + "tapering", + "tapestry", + "tapioca", + "tapping", + "taps", + "tarantula", + "target", + "tarmac", + "tarnish", + "tarot", + "tartar", + "tartly", + "tartness", + "task", + "tassel", + "taste", + "tastiness", + "tasting", + "tasty", + "tattered", + "tattle", + "tattling", + "tattoo", + "taunt", + "tavern", + "thank", + "that", + "thaw", + "theater", + "theatrics", + "thee", + "theft", + "theme", + "theology", + "theorize", + "thermal", + "thermos", + "thesaurus", + "these", + "thesis", + "thespian", + "thicken", + "thicket", + "thickness", + "thieving", + "thievish", + "thigh", + "thimble", + "thing", + "think", + "thinly", + "thinner", + "thinness", + "thinning", + "thirstily", + "thirsting", + "thirsty", + "thirteen", + "thirty", + "thong", + "thorn", + "those", + "thousand", + "thrash", + "thread", + "threaten", + "threefold", + "thrift", + "thrill", + "thrive", + "thriving", + "throat", + "throbbing", + "throng", + "throttle", + "throwaway", + "throwback", + "thrower", + "throwing", + "thud", + "thumb", + "thumping", + "thursday", + "thus", + "thwarting", + "thyself", + "tiara", + "tibia", + "tidal", + "tidbit", + "tidiness", + "tidings", + "tidy", + "tiger", + "tighten", + "tightly", + "tightness", + "tightrope", + "tightwad", + "tigress", + "tile", + "tiling", + "till", + "tilt", + "timid", + "timing", + "timothy", + "tinderbox", + "tinfoil", + "tingle", + "tingling", + "tingly", + "tinker", + "tinkling", + "tinsel", + "tinsmith", + "tint", + "tinwork", + "tiny", + "tipoff", + "tipped", + "tipper", + "tipping", + "tiptoeing", + "tiptop", + "tiring", + "tissue", + "trace", + "tracing", + "track", + "traction", + "tractor", + "trade", + "trading", + "tradition", + "traffic", + "tragedy", + "trailing", + "trailside", + "train", + "traitor", + "trance", + "tranquil", + "transfer", + "transform", + "translate", + "transpire", + "transport", + "transpose", + "trapdoor", + "trapeze", + "trapezoid", + "trapped", + "trapper", + "trapping", + "traps", + "trash", + "travel", + "traverse", + "travesty", + "tray", + "treachery", + "treading", + "treadmill", + "treason", + "treat", + "treble", + "tree", + "trekker", + "tremble", + "trembling", + "tremor", + "trench", + "trend", + "trespass", + "triage", + "trial", + "triangle", + "tribesman", + "tribunal", + "tribune", + "tributary", + "tribute", + "triceps", + "trickery", + "trickily", + "tricking", + "trickle", + "trickster", + "tricky", + "tricolor", + "tricycle", + "trident", + "tried", + "trifle", + "trifocals", + "trillion", + "trilogy", + "trimester", + "trimmer", + "trimming", + "trimness", + "trinity", + "trio", + "tripod", + "tripping", + "triumph", + "trivial", + "trodden", + "trolling", + "trombone", + "trophy", + "tropical", + "tropics", + "trouble", + "troubling", + "trough", + "trousers", + "trout", + "trowel", + "truce", + "truck", + "truffle", + "trump", + "trunks", + "trustable", + "trustee", + "trustful", + "trusting", + "trustless", + "truth", + "try", + "tubby", + "tubeless", + "tubular", + "tucking", + "tuesday", + "tug", + "tuition", + "tulip", + "tumble", + "tumbling", + "tummy", + "turban", + "turbine", + "turbofan", + "turbojet", + "turbulent", + "turf", + "turkey", + "turmoil", + "turret", + "turtle", + "tusk", + "tutor", + "tutu", + "tux", + "tweak", + "tweed", + "tweet", + "tweezers", + "twelve", + "twentieth", + "twenty", + "twerp", + "twice", + "twiddle", + "twiddling", + "twig", + "twilight", + "twine", + "twins", + "twirl", + "twistable", + "twisted", + "twister", + "twisting", + "twisty", + "twitch", + "twitter", + "tycoon", + "tying", + "tyke", + "udder", + "ultimate", + "ultimatum", + "ultra", + "umbilical", + "umbrella", + "umpire", + "unabashed", + "unable", + "unadorned", + "unadvised", + "unafraid", + "unaired", + "unaligned", + "unaltered", + "unarmored", + "unashamed", + "unaudited", + "unawake", + "unaware", + "unbaked", + "unbalance", + "unbeaten", + "unbend", + "unbent", + "unbiased", + "unbitten", + "unblended", + "unblessed", + "unblock", + "unbolted", + "unbounded", + "unboxed", + "unbraided", + "unbridle", + "unbroken", + "unbuckled", + "unbundle", + "unburned", + "unbutton", + "uncanny", + "uncapped", + "uncaring", + "uncertain", + "unchain", + "unchanged", + "uncharted", + "uncheck", + "uncivil", + "unclad", + "unclaimed", + "unclamped", + "unclasp", + "uncle", + "unclip", + "uncloak", + "unclog", + "unclothed", + "uncoated", + "uncoiled", + "uncolored", + "uncombed", + "uncommon", + "uncooked", + "uncork", + "uncorrupt", + "uncounted", + "uncouple", + "uncouth", + "uncover", + "uncross", + "uncrown", + "uncrushed", + "uncured", + "uncurious", + "uncurled", + "uncut", + "undamaged", + "undated", + "undaunted", + "undead", + "undecided", + "undefined", + "underage", + "underarm", + "undercoat", + "undercook", + "undercut", + "underdog", + "underdone", + "underfed", + "underfeed", + "underfoot", + "undergo", + "undergrad", + "underhand", + "underline", + "underling", + "undermine", + "undermost", + "underpaid", + "underpass", + "underpay", + "underrate", + "undertake", + "undertone", + "undertook", + "undertow", + "underuse", + "underwear", + "underwent", + "underwire", + "undesired", + "undiluted", + "undivided", + "undocked", + "undoing", + "undone", + "undrafted", + "undress", + "undrilled", + "undusted", + "undying", + "unearned", + "unearth", + "unease", + "uneasily", + "uneasy", + "uneatable", + "uneaten", + "unedited", + "unelected", + "unending", + "unengaged", + "unenvied", + "unequal", + "unethical", + "uneven", + "unexpired", + "unexposed", + "unfailing", + "unfair", + "unfasten", + "unfazed", + "unfeeling", + "unfiled", + "unfilled", + "unfitted", + "unfitting", + "unfixable", + "unfixed", + "unflawed", + "unfocused", + "unfold", + "unfounded", + "unframed", + "unfreeze", + "unfrosted", + "unfrozen", + "unfunded", + "unglazed", + "ungloved", + "unglue", + "ungodly", + "ungraded", + "ungreased", + "unguarded", + "unguided", + "unhappily", + "unhappy", + "unharmed", + "unhealthy", + "unheard", + "unhearing", + "unheated", + "unhelpful", + "unhidden", + "unhinge", + "unhitched", + "unholy", + "unhook", + "unicorn", + "unicycle", + "unified", + "unifier", + "uniformed", + "uniformly", + "unify", + "unimpeded", + "uninjured", + "uninstall", + "uninsured", + "uninvited", + "union", + "uniquely", + "unisexual", + "unison", + "unissued", + "unit", + "universal", + "universe", + "unjustly", + "unkempt", + "unkind", + "unknotted", + "unknowing", + "unknown", + "unlaced", + "unlatch", + "unlawful", + "unleaded", + "unlearned", + "unleash", + "unless", + "unleveled", + "unlighted", + "unlikable", + "unlimited", + "unlined", + "unlinked", + "unlisted", + "unlit", + "unlivable", + "unloaded", + "unloader", + "unlocked", + "unlocking", + "unlovable", + "unloved", + "unlovely", + "unloving", + "unluckily", + "unlucky", + "unmade", + "unmanaged", + "unmanned", + "unmapped", + "unmarked", + "unmasked", + "unmasking", + "unmatched", + "unmindful", + "unmixable", + "unmixed", + "unmolded", + "unmoral", + "unmovable", + "unmoved", + "unmoving", + "unnamable", + "unnamed", + "unnatural", + "unneeded", + "unnerve", + "unnerving", + "unnoticed", + "unopened", + "unopposed", + "unpack", + "unpadded", + "unpaid", + "unpainted", + "unpaired", + "unpaved", + "unpeeled", + "unpicked", + "unpiloted", + "unpinned", + "unplanned", + "unplanted", + "unpleased", + "unpledged", + "unplowed", + "unplug", + "unpopular", + "unproven", + "unquote", + "unranked", + "unrated", + "unraveled", + "unreached", + "unread", + "unreal", + "unreeling", + "unrefined", + "unrelated", + "unrented", + "unrest", + "unretired", + "unrevised", + "unrigged", + "unripe", + "unrivaled", + "unroasted", + "unrobed", + "unroll", + "unruffled", + "unruly", + "unrushed", + "unsaddle", + "unsafe", + "unsaid", + "unsalted", + "unsaved", + "unsavory", + "unscathed", + "unscented", + "unscrew", + "unsealed", + "unseated", + "unsecured", + "unseeing", + "unseemly", + "unseen", + "unselect", + "unselfish", + "unsent", + "unsettled", + "unshackle", + "unshaken", + "unshaved", + "unshaven", + "unsheathe", + "unshipped", + "unsightly", + "unsigned", + "unskilled", + "unsliced", + "unsmooth", + "unsnap", + "unsocial", + "unsoiled", + "unsold", + "unsolved", + "unsorted", + "unspoiled", + "unspoken", + "unstable", + "unstaffed", + "unstamped", + "unsteady", + "unsterile", + "unstirred", + "unstitch", + "unstopped", + "unstuck", + "unstuffed", + "unstylish", + "unsubtle", + "unsubtly", + "unsuited", + "unsure", + "unsworn", + "untagged", + "untainted", + "untaken", + "untamed", + "untangled", + "untapped", + "untaxed", + "unthawed", + "unthread", + "untidy", + "untie", + "until", + "untimed", + "untimely", + "untitled", + "untoasted", + "untold", + "untouched", + "untracked", + "untrained", + "untreated", + "untried", + "untrimmed", + "untrue", + "untruth", + "unturned", + "untwist", + "untying", + "unusable", + "unused", + "unusual", + "unvalued", + "unvaried", + "unvarying", + "unveiled", + "unveiling", + "unvented", + "unviable", + "unvisited", + "unvocal", + "unwanted", + "unwarlike", + "unwary", + "unwashed", + "unwatched", + "unweave", + "unwed", + "unwelcome", + "unwell", + "unwieldy", + "unwilling", + "unwind", + "unwired", + "unwitting", + "unwomanly", + "unworldly", + "unworn", + "unworried", + "unworthy", + "unwound", + "unwoven", + "unwrapped", + "unwritten", + "unzip", + "upbeat", + "upchuck", + "upcoming", + "upcountry", + "update", + "upfront", + "upgrade", + "upheaval", + "upheld", + "uphill", + "uphold", + "uplifted", + "uplifting", + "upload", + "upon", + "upper", + "upright", + "uprising", + "upriver", + "uproar", + "uproot", + "upscale", + "upside", + "upstage", + "upstairs", + "upstart", + "upstate", + "upstream", + "upstroke", + "upswing", + "uptake", + "uptight", + "uptown", + "upturned", + "upward", + "upwind", + "uranium", + "urban", + "urchin", + "urethane", + "urgency", + "urgent", + "urging", + "urologist", + "urology", + "usable", + "usage", + "useable", + "used", + "uselessly", + "user", + "usher", + "usual", + "utensil", + "utility", + "utilize", + "utmost", + "utopia", + "utter", + "vacancy", + "vacant", + "vacate", + "vacation", + "vagabond", + "vagrancy", + "vagrantly", + "vaguely", + "vagueness", + "valiant", + "valid", + "valium", + "valley", + "valuables", + "value", + "vanilla", + "vanish", + "vanity", + "vanquish", + "vantage", + "vaporizer", + "variable", + "variably", + "varied", + "variety", + "various", + "varmint", + "varnish", + "varsity", + "varying", + "vascular", + "vaseline", + "vastly", + "vastness", + "veal", + "vegan", + "veggie", + "vehicular", + "velcro", + "velocity", + "velvet", + "vendetta", + "vending", + "vendor", + "veneering", + "vengeful", + "venomous", + "ventricle", + "venture", + "venue", + "venus", + "verbalize", + "verbally", + "verbose", + "verdict", + "verify", + "verse", + "version", + "versus", + "vertebrae", + "vertical", + "vertigo", + "very", + "vessel", + "vest", + "veteran", + "veto", + "vexingly", + "viability", + "viable", + "vibes", + "vice", + "vicinity", + "victory", + "video", + "viewable", + "viewer", + "viewing", + "viewless", + "viewpoint", + "vigorous", + "village", + "villain", + "vindicate", + "vineyard", + "vintage", + "violate", + "violation", + "violator", + "violet", + "violin", + "viper", + "viral", + "virtual", + "virtuous", + "virus", + "visa", + "viscosity", + "viscous", + "viselike", + "visible", + "visibly", + "vision", + "visiting", + "visitor", + "visor", + "vista", + "vitality", + "vitalize", + "vitally", + "vitamins", + "vivacious", + "vividly", + "vividness", + "vixen", + "vocalist", + "vocalize", + "vocally", + "vocation", + "voice", + "voicing", + "void", + "volatile", + "volley", + "voltage", + "volumes", + "voter", + "voting", + "voucher", + "vowed", + "vowel", + "voyage", + "wackiness", + "wad", + "wafer", + "waffle", + "waged", + "wager", + "wages", + "waggle", + "wagon", + "wake", + "waking", + "walk", + "walmart", + "walnut", + "walrus", + "waltz", + "wand", + "wannabe", + "wanted", + "wanting", + "wasabi", + "washable", + "washbasin", + "washboard", + "washbowl", + "washcloth", + "washday", + "washed", + "washer", + "washhouse", + "washing", + "washout", + "washroom", + "washstand", + "washtub", + "wasp", + "wasting", + "watch", + "water", + "waviness", + "waving", + "wavy", + "whacking", + "whacky", + "wham", + "wharf", + "wheat", + "whenever", + "whiff", + "whimsical", + "whinny", + "whiny", + "whisking", + "whoever", + "whole", + "whomever", + "whoopee", + "whooping", + "whoops", + "why", + "wick", + "widely", + "widen", + "widget", + "widow", + "width", + "wieldable", + "wielder", + "wife", + "wifi", + "wikipedia", + "wildcard", + "wildcat", + "wilder", + "wildfire", + "wildfowl", + "wildland", + "wildlife", + "wildly", + "wildness", + "willed", + "willfully", + "willing", + "willow", + "willpower", + "wilt", + "wimp", + "wince", + "wincing", + "wind", + "wing", + "winking", + "winner", + "winnings", + "winter", + "wipe", + "wired", + "wireless", + "wiring", + "wiry", + "wisdom", + "wise", + "wish", + "wisplike", + "wispy", + "wistful", + "wizard", + "wobble", + "wobbling", + "wobbly", + "wok", + "wolf", + "wolverine", + "womanhood", + "womankind", + "womanless", + "womanlike", + "womanly", + "womb", + "woof", + "wooing", + "wool", + "woozy", + "word", + "work", + "worried", + "worrier", + "worrisome", + "worry", + "worsening", + "worshiper", + "worst", + "wound", + "woven", + "wow", + "wrangle", + "wrath", + "wreath", + "wreckage", + "wrecker", + "wrecking", + "wrench", + "wriggle", + "wriggly", + "wrinkle", + "wrinkly", + "wrist", + "writing", + "written", + "wrongdoer", + "wronged", + "wrongful", + "wrongly", + "wrongness", + "wrought", + "xbox", + "xerox", + "yahoo", + "yam", + "yanking", + "yapping", + "yard", + "yarn", + "yeah", + "yearbook", + "yearling", + "yearly", + "yearning", + "yeast", + "yelling", + "yelp", + "yen", + "yesterday", + "yiddish", + "yield", + "yin", + "yippee", + "yo-yo", + "yodel", + "yoga", + "yogurt", + "yonder", + "yoyo", + "yummy", + "zap", + "zealous", + "zebra", + "zen", + "zeppelin", + "zero", + "zestfully", + "zesty", + "zigzagged", + "zipfile", + "zipping", + "zippy", + "zips", + "zit", + "zodiac", + "zombie", + "zone", + "zoning", + "zookeeper", + "zoologist", + "zoology", + "zoom", +]; diff --git a/jslib/common/src/models/api/cardApi.ts b/jslib/common/src/models/api/cardApi.ts new file mode 100644 index 00000000..8d972624 --- /dev/null +++ b/jslib/common/src/models/api/cardApi.ts @@ -0,0 +1,23 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class CardApi extends BaseResponse { + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.cardholderName = this.getResponseProperty("CardholderName"); + this.brand = this.getResponseProperty("Brand"); + this.number = this.getResponseProperty("Number"); + this.expMonth = this.getResponseProperty("ExpMonth"); + this.expYear = this.getResponseProperty("ExpYear"); + this.code = this.getResponseProperty("Code"); + } +} diff --git a/jslib/common/src/models/api/fieldApi.ts b/jslib/common/src/models/api/fieldApi.ts new file mode 100644 index 00000000..cabdb7cc --- /dev/null +++ b/jslib/common/src/models/api/fieldApi.ts @@ -0,0 +1,21 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { BaseResponse } from "../response/baseResponse"; + +export class FieldApi extends BaseResponse { + name: string; + value: string; + type: FieldType; + linkedId: LinkedIdType; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.value = this.getResponseProperty("Value"); + this.linkedId = this.getResponseProperty("linkedId"); + } +} diff --git a/jslib/common/src/models/api/identityApi.ts b/jslib/common/src/models/api/identityApi.ts new file mode 100644 index 00000000..9312438f --- /dev/null +++ b/jslib/common/src/models/api/identityApi.ts @@ -0,0 +1,47 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class IdentityApi extends BaseResponse { + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.title = this.getResponseProperty("Title"); + this.firstName = this.getResponseProperty("FirstName"); + this.middleName = this.getResponseProperty("MiddleName"); + this.lastName = this.getResponseProperty("LastName"); + this.address1 = this.getResponseProperty("Address1"); + this.address2 = this.getResponseProperty("Address2"); + this.address3 = this.getResponseProperty("Address3"); + this.city = this.getResponseProperty("City"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.country = this.getResponseProperty("Country"); + this.company = this.getResponseProperty("Company"); + this.email = this.getResponseProperty("Email"); + this.phone = this.getResponseProperty("Phone"); + this.ssn = this.getResponseProperty("SSN"); + this.username = this.getResponseProperty("Username"); + this.passportNumber = this.getResponseProperty("PassportNumber"); + this.licenseNumber = this.getResponseProperty("LicenseNumber"); + } +} diff --git a/jslib/common/src/models/api/loginApi.ts b/jslib/common/src/models/api/loginApi.ts new file mode 100644 index 00000000..ddbe37ec --- /dev/null +++ b/jslib/common/src/models/api/loginApi.ts @@ -0,0 +1,29 @@ +import { BaseResponse } from "../response/baseResponse"; + +import { LoginUriApi } from "./loginUriApi"; + +export class LoginApi extends BaseResponse { + uris: LoginUriApi[]; + username: string; + password: string; + passwordRevisionDate: string; + totp: string; + autofillOnPageLoad: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.username = this.getResponseProperty("Username"); + this.password = this.getResponseProperty("Password"); + this.passwordRevisionDate = this.getResponseProperty("PasswordRevisionDate"); + this.totp = this.getResponseProperty("Totp"); + this.autofillOnPageLoad = this.getResponseProperty("AutofillOnPageLoad"); + + const uris = this.getResponseProperty("Uris"); + if (uris != null) { + this.uris = uris.map((u: any) => new LoginUriApi(u)); + } + } +} diff --git a/jslib/common/src/models/api/loginUriApi.ts b/jslib/common/src/models/api/loginUriApi.ts new file mode 100644 index 00000000..50ffb0da --- /dev/null +++ b/jslib/common/src/models/api/loginUriApi.ts @@ -0,0 +1,17 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { BaseResponse } from "../response/baseResponse"; + +export class LoginUriApi extends BaseResponse { + uri: string; + match: UriMatchType = null; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.uri = this.getResponseProperty("Uri"); + const match = this.getResponseProperty("Match"); + this.match = match != null ? match : null; + } +} diff --git a/jslib/common/src/models/api/permissionsApi.ts b/jslib/common/src/models/api/permissionsApi.ts new file mode 100644 index 00000000..bac79bd3 --- /dev/null +++ b/jslib/common/src/models/api/permissionsApi.ts @@ -0,0 +1,55 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class PermissionsApi extends BaseResponse { + accessEventLogs: boolean; + accessImportExport: boolean; + accessReports: boolean; + /** + * @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and + * `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + manageAllCollections: boolean; + createNewCollections: boolean; + editAnyCollection: boolean; + deleteAnyCollection: boolean; + /** + * @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and + * `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0 + */ + manageAssignedCollections: boolean; + editAssignedCollections: boolean; + deleteAssignedCollections: boolean; + manageCiphers: boolean; + manageGroups: boolean; + manageSso: boolean; + managePolicies: boolean; + manageUsers: boolean; + manageResetPassword: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return this; + } + this.accessEventLogs = this.getResponseProperty("AccessEventLogs"); + this.accessImportExport = this.getResponseProperty("AccessImportExport"); + this.accessReports = this.getResponseProperty("AccessReports"); + + // For backwards compatibility with Server <= 1.43.0 + this.manageAllCollections = this.getResponseProperty("ManageAllCollections"); + this.manageAssignedCollections = this.getResponseProperty("ManageAssignedCollections"); + + this.createNewCollections = this.getResponseProperty("CreateNewCollections"); + this.editAnyCollection = this.getResponseProperty("EditAnyCollection"); + this.deleteAnyCollection = this.getResponseProperty("DeleteAnyCollection"); + this.editAssignedCollections = this.getResponseProperty("EditAssignedCollections"); + this.deleteAssignedCollections = this.getResponseProperty("DeleteAssignedCollections"); + + this.manageCiphers = this.getResponseProperty("ManageCiphers"); + this.manageGroups = this.getResponseProperty("ManageGroups"); + this.manageSso = this.getResponseProperty("ManageSso"); + this.managePolicies = this.getResponseProperty("ManagePolicies"); + this.manageUsers = this.getResponseProperty("ManageUsers"); + this.manageResetPassword = this.getResponseProperty("ManageResetPassword"); + } +} diff --git a/jslib/common/src/models/api/secureNoteApi.ts b/jslib/common/src/models/api/secureNoteApi.ts new file mode 100644 index 00000000..80f18c61 --- /dev/null +++ b/jslib/common/src/models/api/secureNoteApi.ts @@ -0,0 +1,14 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { BaseResponse } from "../response/baseResponse"; + +export class SecureNoteApi extends BaseResponse { + type: SecureNoteType; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.type = this.getResponseProperty("Type"); + } +} diff --git a/jslib/common/src/models/api/sendFileApi.ts b/jslib/common/src/models/api/sendFileApi.ts new file mode 100644 index 00000000..99fa0ff5 --- /dev/null +++ b/jslib/common/src/models/api/sendFileApi.ts @@ -0,0 +1,19 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class SendFileApi extends BaseResponse { + id: string; + fileName: string; + size: string; + sizeName: string; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.id = this.getResponseProperty("Id"); + this.fileName = this.getResponseProperty("FileName"); + this.size = this.getResponseProperty("Size"); + this.sizeName = this.getResponseProperty("SizeName"); + } +} diff --git a/jslib/common/src/models/api/sendTextApi.ts b/jslib/common/src/models/api/sendTextApi.ts new file mode 100644 index 00000000..56d21450 --- /dev/null +++ b/jslib/common/src/models/api/sendTextApi.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "../response/baseResponse"; + +export class SendTextApi extends BaseResponse { + text: string; + hidden: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + this.text = this.getResponseProperty("Text"); + this.hidden = this.getResponseProperty("Hidden") || false; + } +} diff --git a/jslib/common/src/models/api/ssoConfigApi.ts b/jslib/common/src/models/api/ssoConfigApi.ts new file mode 100644 index 00000000..0767f3d6 --- /dev/null +++ b/jslib/common/src/models/api/ssoConfigApi.ts @@ -0,0 +1,136 @@ +import { + OpenIdConnectRedirectBehavior, + Saml2BindingType, + Saml2NameIdFormat, + Saml2SigningBehavior, + SsoType, +} from "../../enums/ssoEnums"; +import { BaseResponse } from "../response/baseResponse"; +import { SsoConfigView } from "../view/ssoConfigView"; + +export class SsoConfigApi extends BaseResponse { + static fromView(view: SsoConfigView, api = new SsoConfigApi()) { + api.configType = view.configType; + + api.keyConnectorEnabled = view.keyConnectorEnabled; + api.keyConnectorUrl = view.keyConnectorUrl; + + if (api.configType === SsoType.OpenIdConnect) { + api.authority = view.openId.authority; + api.clientId = view.openId.clientId; + api.clientSecret = view.openId.clientSecret; + api.metadataAddress = view.openId.metadataAddress; + api.redirectBehavior = view.openId.redirectBehavior; + api.getClaimsFromUserInfoEndpoint = view.openId.getClaimsFromUserInfoEndpoint; + api.additionalScopes = view.openId.additionalScopes; + api.additionalUserIdClaimTypes = view.openId.additionalUserIdClaimTypes; + api.additionalEmailClaimTypes = view.openId.additionalEmailClaimTypes; + api.additionalNameClaimTypes = view.openId.additionalNameClaimTypes; + api.acrValues = view.openId.acrValues; + api.expectedReturnAcrValue = view.openId.expectedReturnAcrValue; + } else if (api.configType === SsoType.Saml2) { + api.spNameIdFormat = view.saml.spNameIdFormat; + api.spOutboundSigningAlgorithm = view.saml.spOutboundSigningAlgorithm; + api.spSigningBehavior = view.saml.spSigningBehavior; + api.spMinIncomingSigningAlgorithm = view.saml.spMinIncomingSigningAlgorithm; + api.spWantAssertionsSigned = view.saml.spWantAssertionsSigned; + api.spValidateCertificates = view.saml.spValidateCertificates; + + api.idpEntityId = view.saml.idpEntityId; + api.idpBindingType = view.saml.idpBindingType; + api.idpSingleSignOnServiceUrl = view.saml.idpSingleSignOnServiceUrl; + api.idpSingleLogoutServiceUrl = view.saml.idpSingleLogoutServiceUrl; + api.idpX509PublicCert = view.saml.idpX509PublicCert; + api.idpOutboundSigningAlgorithm = view.saml.idpOutboundSigningAlgorithm; + api.idpAllowUnsolicitedAuthnResponse = view.saml.idpAllowUnsolicitedAuthnResponse; + api.idpWantAuthnRequestsSigned = view.saml.idpWantAuthnRequestsSigned; + + // Value is inverted in the api model (disable instead of allow) + api.idpDisableOutboundLogoutRequests = !view.saml.idpAllowOutboundLogoutRequests; + } + + return api; + } + configType: SsoType; + + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + // OpenId + authority: string; + clientId: string; + clientSecret: string; + metadataAddress: string; + redirectBehavior: OpenIdConnectRedirectBehavior; + getClaimsFromUserInfoEndpoint: boolean; + additionalScopes: string; + additionalUserIdClaimTypes: string; + additionalEmailClaimTypes: string; + additionalNameClaimTypes: string; + acrValues: string; + expectedReturnAcrValue: string; + + // SAML + spNameIdFormat: Saml2NameIdFormat; + spOutboundSigningAlgorithm: string; + spSigningBehavior: Saml2SigningBehavior; + spMinIncomingSigningAlgorithm: boolean; + spWantAssertionsSigned: boolean; + spValidateCertificates: boolean; + + idpEntityId: string; + idpBindingType: Saml2BindingType; + idpSingleSignOnServiceUrl: string; + idpSingleLogoutServiceUrl: string; + idpX509PublicCert: string; + idpOutboundSigningAlgorithm: string; + idpAllowUnsolicitedAuthnResponse: boolean; + idpDisableOutboundLogoutRequests: boolean; + idpWantAuthnRequestsSigned: boolean; + + constructor(data: any = null) { + super(data); + if (data == null) { + return; + } + + this.configType = this.getResponseProperty("ConfigType"); + + this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled"); + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + + this.authority = this.getResponseProperty("Authority"); + this.clientId = this.getResponseProperty("ClientId"); + this.clientSecret = this.getResponseProperty("ClientSecret"); + this.metadataAddress = this.getResponseProperty("MetadataAddress"); + this.redirectBehavior = this.getResponseProperty("RedirectBehavior"); + this.getClaimsFromUserInfoEndpoint = this.getResponseProperty("GetClaimsFromUserInfoEndpoint"); + this.additionalScopes = this.getResponseProperty("AdditionalScopes"); + this.additionalUserIdClaimTypes = this.getResponseProperty("AdditionalUserIdClaimTypes"); + this.additionalEmailClaimTypes = this.getResponseProperty("AdditionalEmailClaimTypes"); + this.additionalNameClaimTypes = this.getResponseProperty("AdditionalNameClaimTypes"); + this.acrValues = this.getResponseProperty("AcrValues"); + this.expectedReturnAcrValue = this.getResponseProperty("ExpectedReturnAcrValue"); + + this.spNameIdFormat = this.getResponseProperty("SpNameIdFormat"); + this.spOutboundSigningAlgorithm = this.getResponseProperty("SpOutboundSigningAlgorithm"); + this.spSigningBehavior = this.getResponseProperty("SpSigningBehavior"); + this.spMinIncomingSigningAlgorithm = this.getResponseProperty("SpMinIncomingSigningAlgorithm"); + this.spWantAssertionsSigned = this.getResponseProperty("SpWantAssertionsSigned"); + this.spValidateCertificates = this.getResponseProperty("SpValidateCertificates"); + + this.idpEntityId = this.getResponseProperty("IdpEntityId"); + this.idpBindingType = this.getResponseProperty("IdpBindingType"); + this.idpSingleSignOnServiceUrl = this.getResponseProperty("IdpSingleSignOnServiceUrl"); + this.idpSingleLogoutServiceUrl = this.getResponseProperty("IdpSingleLogoutServiceUrl"); + this.idpX509PublicCert = this.getResponseProperty("IdpX509PublicCert"); + this.idpOutboundSigningAlgorithm = this.getResponseProperty("IdpOutboundSigningAlgorithm"); + this.idpAllowUnsolicitedAuthnResponse = this.getResponseProperty( + "IdpAllowUnsolicitedAuthnResponse" + ); + this.idpDisableOutboundLogoutRequests = this.getResponseProperty( + "IdpDisableOutboundLogoutRequests" + ); + this.idpWantAuthnRequestsSigned = this.getResponseProperty("IdpWantAuthnRequestsSigned"); + } +} diff --git a/jslib/common/src/models/data/attachmentData.ts b/jslib/common/src/models/data/attachmentData.ts new file mode 100644 index 00000000..50af03ac --- /dev/null +++ b/jslib/common/src/models/data/attachmentData.ts @@ -0,0 +1,22 @@ +import { AttachmentResponse } from "../response/attachmentResponse"; + +export class AttachmentData { + id: string; + url: string; + fileName: string; + key: string; + size: string; + sizeName: string; + + constructor(response?: AttachmentResponse) { + if (response == null) { + return; + } + this.id = response.id; + this.url = response.url; + this.fileName = response.fileName; + this.key = response.key; + this.size = response.size; + this.sizeName = response.sizeName; + } +} diff --git a/jslib/common/src/models/data/cardData.ts b/jslib/common/src/models/data/cardData.ts new file mode 100644 index 00000000..9d90e4b2 --- /dev/null +++ b/jslib/common/src/models/data/cardData.ts @@ -0,0 +1,23 @@ +import { CardApi } from "../api/cardApi"; + +export class CardData { + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(data?: CardApi) { + if (data == null) { + return; + } + + this.cardholderName = data.cardholderName; + this.brand = data.brand; + this.number = data.number; + this.expMonth = data.expMonth; + this.expYear = data.expYear; + this.code = data.code; + } +} diff --git a/jslib/common/src/models/data/cipherData.ts b/jslib/common/src/models/data/cipherData.ts new file mode 100644 index 00000000..7ebc7070 --- /dev/null +++ b/jslib/common/src/models/data/cipherData.ts @@ -0,0 +1,85 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CipherResponse } from "../response/cipherResponse"; + +import { AttachmentData } from "./attachmentData"; +import { CardData } from "./cardData"; +import { FieldData } from "./fieldData"; +import { IdentityData } from "./identityData"; +import { LoginData } from "./loginData"; +import { PasswordHistoryData } from "./passwordHistoryData"; +import { SecureNoteData } from "./secureNoteData"; + +export class CipherData { + id: string; + organizationId: string; + folderId: string; + userId: string; + edit: boolean; + viewPassword: boolean; + organizationUseTotp: boolean; + favorite: boolean; + revisionDate: string; + type: CipherType; + name: string; + notes: string; + login?: LoginData; + secureNote?: SecureNoteData; + card?: CardData; + identity?: IdentityData; + fields?: FieldData[]; + attachments?: AttachmentData[]; + passwordHistory?: PasswordHistoryData[]; + collectionIds?: string[]; + deletedDate: string; + reprompt: CipherRepromptType; + + constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) { + if (response == null) { + return; + } + + this.id = response.id; + this.organizationId = response.organizationId; + this.folderId = response.folderId; + this.userId = userId; + this.edit = response.edit; + this.viewPassword = response.viewPassword; + this.organizationUseTotp = response.organizationUseTotp; + this.favorite = response.favorite; + this.revisionDate = response.revisionDate; + this.type = response.type; + this.name = response.name; + this.notes = response.notes; + this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds; + this.deletedDate = response.deletedDate; + this.reprompt = response.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new LoginData(response.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNoteData(response.secureNote); + break; + case CipherType.Card: + this.card = new CardData(response.card); + break; + case CipherType.Identity: + this.identity = new IdentityData(response.identity); + break; + default: + break; + } + + if (response.fields != null) { + this.fields = response.fields.map((f) => new FieldData(f)); + } + if (response.attachments != null) { + this.attachments = response.attachments.map((a) => new AttachmentData(a)); + } + if (response.passwordHistory != null) { + this.passwordHistory = response.passwordHistory.map((ph) => new PasswordHistoryData(ph)); + } + } +} diff --git a/jslib/common/src/models/data/collectionData.ts b/jslib/common/src/models/data/collectionData.ts new file mode 100644 index 00000000..9e2607ed --- /dev/null +++ b/jslib/common/src/models/data/collectionData.ts @@ -0,0 +1,17 @@ +import { CollectionDetailsResponse } from "../response/collectionResponse"; + +export class CollectionData { + id: string; + organizationId: string; + name: string; + externalId: string; + readOnly: boolean; + + constructor(response: CollectionDetailsResponse) { + this.id = response.id; + this.organizationId = response.organizationId; + this.name = response.name; + this.externalId = response.externalId; + this.readOnly = response.readOnly; + } +} diff --git a/jslib/common/src/models/data/eventData.ts b/jslib/common/src/models/data/eventData.ts new file mode 100644 index 00000000..c0e38ddc --- /dev/null +++ b/jslib/common/src/models/data/eventData.ts @@ -0,0 +1,7 @@ +import { EventType } from "../../enums/eventType"; + +export class EventData { + type: EventType; + cipherId: string; + date: string; +} diff --git a/jslib/common/src/models/data/fieldData.ts b/jslib/common/src/models/data/fieldData.ts new file mode 100644 index 00000000..6bec9def --- /dev/null +++ b/jslib/common/src/models/data/fieldData.ts @@ -0,0 +1,20 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { FieldApi } from "../api/fieldApi"; + +export class FieldData { + type: FieldType; + name: string; + value: string; + linkedId: LinkedIdType; + + constructor(response?: FieldApi) { + if (response == null) { + return; + } + this.type = response.type; + this.name = response.name; + this.value = response.value; + this.linkedId = response.linkedId; + } +} diff --git a/jslib/common/src/models/data/folderData.ts b/jslib/common/src/models/data/folderData.ts new file mode 100644 index 00000000..459ba4de --- /dev/null +++ b/jslib/common/src/models/data/folderData.ts @@ -0,0 +1,15 @@ +import { FolderResponse } from "../response/folderResponse"; + +export class FolderData { + id: string; + userId: string; + name: string; + revisionDate: string; + + constructor(response: FolderResponse, userId: string) { + this.userId = userId; + this.name = response.name; + this.id = response.id; + this.revisionDate = response.revisionDate; + } +} diff --git a/jslib/common/src/models/data/identityData.ts b/jslib/common/src/models/data/identityData.ts new file mode 100644 index 00000000..02aa7eb6 --- /dev/null +++ b/jslib/common/src/models/data/identityData.ts @@ -0,0 +1,47 @@ +import { IdentityApi } from "../api/identityApi"; + +export class IdentityData { + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(data?: IdentityApi) { + if (data == null) { + return; + } + + this.title = data.title; + this.firstName = data.firstName; + this.middleName = data.middleName; + this.lastName = data.lastName; + this.address1 = data.address1; + this.address2 = data.address2; + this.address3 = data.address3; + this.city = data.city; + this.state = data.state; + this.postalCode = data.postalCode; + this.country = data.country; + this.company = data.company; + this.email = data.email; + this.phone = data.phone; + this.ssn = data.ssn; + this.username = data.username; + this.passportNumber = data.passportNumber; + this.licenseNumber = data.licenseNumber; + } +} diff --git a/jslib/common/src/models/data/loginData.ts b/jslib/common/src/models/data/loginData.ts new file mode 100644 index 00000000..e51180c8 --- /dev/null +++ b/jslib/common/src/models/data/loginData.ts @@ -0,0 +1,28 @@ +import { LoginApi } from "../api/loginApi"; + +import { LoginUriData } from "./loginUriData"; + +export class LoginData { + uris: LoginUriData[]; + username: string; + password: string; + passwordRevisionDate: string; + totp: string; + autofillOnPageLoad: boolean; + + constructor(data?: LoginApi) { + if (data == null) { + return; + } + + this.username = data.username; + this.password = data.password; + this.passwordRevisionDate = data.passwordRevisionDate; + this.totp = data.totp; + this.autofillOnPageLoad = data.autofillOnPageLoad; + + if (data.uris) { + this.uris = data.uris.map((u) => new LoginUriData(u)); + } + } +} diff --git a/jslib/common/src/models/data/loginUriData.ts b/jslib/common/src/models/data/loginUriData.ts new file mode 100644 index 00000000..874db4e8 --- /dev/null +++ b/jslib/common/src/models/data/loginUriData.ts @@ -0,0 +1,15 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { LoginUriApi } from "../api/loginUriApi"; + +export class LoginUriData { + uri: string; + match: UriMatchType = null; + + constructor(data?: LoginUriApi) { + if (data == null) { + return; + } + this.uri = data.uri; + this.match = data.match; + } +} diff --git a/jslib/common/src/models/data/organizationData.ts b/jslib/common/src/models/data/organizationData.ts new file mode 100644 index 00000000..fe190fff --- /dev/null +++ b/jslib/common/src/models/data/organizationData.ts @@ -0,0 +1,78 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; +import { ProfileOrganizationResponse } from "../response/profileOrganizationResponse"; + +export class OrganizationData { + id: string; + name: string; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + hasPublicAndPrivateKeys: boolean; + providerId: string; + providerName: string; + isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(response: ProfileOrganizationResponse) { + this.id = response.id; + this.name = response.name; + this.status = response.status; + this.type = response.type; + this.enabled = response.enabled; + this.usePolicies = response.usePolicies; + this.useGroups = response.useGroups; + this.useDirectory = response.useDirectory; + this.useEvents = response.useEvents; + this.useTotp = response.useTotp; + this.use2fa = response.use2fa; + this.useApi = response.useApi; + this.useSso = response.useSso; + this.useKeyConnector = response.useKeyConnector; + this.useResetPassword = response.useResetPassword; + this.selfHost = response.selfHost; + this.usersGetPremium = response.usersGetPremium; + this.seats = response.seats; + this.maxCollections = response.maxCollections; + this.maxStorageGb = response.maxStorageGb; + this.ssoBound = response.ssoBound; + this.identifier = response.identifier; + this.permissions = response.permissions; + this.resetPasswordEnrolled = response.resetPasswordEnrolled; + this.userId = response.userId; + this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys; + this.providerId = response.providerId; + this.providerName = response.providerName; + this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = response.familySponsorshipAvailable; + this.planProductType = response.planProductType; + this.keyConnectorEnabled = response.keyConnectorEnabled; + this.keyConnectorUrl = response.keyConnectorUrl; + } +} diff --git a/jslib/common/src/models/data/passwordHistoryData.ts b/jslib/common/src/models/data/passwordHistoryData.ts new file mode 100644 index 00000000..72436f3d --- /dev/null +++ b/jslib/common/src/models/data/passwordHistoryData.ts @@ -0,0 +1,15 @@ +import { PasswordHistoryResponse } from "../response/passwordHistoryResponse"; + +export class PasswordHistoryData { + password: string; + lastUsedDate: string; + + constructor(response?: PasswordHistoryResponse) { + if (response == null) { + return; + } + + this.password = response.password; + this.lastUsedDate = response.lastUsedDate; + } +} diff --git a/jslib/common/src/models/data/policyData.ts b/jslib/common/src/models/data/policyData.ts new file mode 100644 index 00000000..c93b4040 --- /dev/null +++ b/jslib/common/src/models/data/policyData.ts @@ -0,0 +1,18 @@ +import { PolicyType } from "../../enums/policyType"; +import { PolicyResponse } from "../response/policyResponse"; + +export class PolicyData { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(response: PolicyResponse) { + this.id = response.id; + this.organizationId = response.organizationId; + this.type = response.type; + this.data = response.data; + this.enabled = response.enabled; + } +} diff --git a/jslib/common/src/models/data/providerData.ts b/jslib/common/src/models/data/providerData.ts new file mode 100644 index 00000000..5d141ed1 --- /dev/null +++ b/jslib/common/src/models/data/providerData.ts @@ -0,0 +1,23 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { ProfileProviderResponse } from "../response/profileProviderResponse"; + +export class ProviderData { + id: string; + name: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + userId: string; + useEvents: boolean; + + constructor(response: ProfileProviderResponse) { + this.id = response.id; + this.name = response.name; + this.status = response.status; + this.type = response.type; + this.enabled = response.enabled; + this.userId = response.userId; + this.useEvents = response.useEvents; + } +} diff --git a/jslib/common/src/models/data/secureNoteData.ts b/jslib/common/src/models/data/secureNoteData.ts new file mode 100644 index 00000000..f536b2de --- /dev/null +++ b/jslib/common/src/models/data/secureNoteData.ts @@ -0,0 +1,14 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNoteApi } from "../api/secureNoteApi"; + +export class SecureNoteData { + type: SecureNoteType; + + constructor(data?: SecureNoteApi) { + if (data == null) { + return; + } + + this.type = data.type; + } +} diff --git a/jslib/common/src/models/data/sendData.ts b/jslib/common/src/models/data/sendData.ts new file mode 100644 index 00000000..18c007e3 --- /dev/null +++ b/jslib/common/src/models/data/sendData.ts @@ -0,0 +1,58 @@ +import { SendType } from "../../enums/sendType"; +import { SendResponse } from "../response/sendResponse"; + +import { SendFileData } from "./sendFileData"; +import { SendTextData } from "./sendTextData"; + +export class SendData { + id: string; + accessId: string; + userId: string; + type: SendType; + name: string; + notes: string; + file: SendFileData; + text: SendTextData; + key: string; + maxAccessCount?: number; + accessCount: number; + revisionDate: string; + expirationDate: string; + deletionDate: string; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(response?: SendResponse, userId?: string) { + if (response == null) { + return; + } + + this.id = response.id; + this.accessId = response.accessId; + this.userId = userId; + this.type = response.type; + this.name = response.name; + this.notes = response.notes; + this.key = response.key; + this.maxAccessCount = response.maxAccessCount; + this.accessCount = response.accessCount; + this.revisionDate = response.revisionDate; + this.expirationDate = response.expirationDate; + this.deletionDate = response.deletionDate; + this.password = response.password; + this.disabled = response.disable; + this.hideEmail = response.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendTextData(response.text); + break; + case SendType.File: + this.file = new SendFileData(response.file); + break; + default: + break; + } + } +} diff --git a/jslib/common/src/models/data/sendFileData.ts b/jslib/common/src/models/data/sendFileData.ts new file mode 100644 index 00000000..aaf868c1 --- /dev/null +++ b/jslib/common/src/models/data/sendFileData.ts @@ -0,0 +1,19 @@ +import { SendFileApi } from "../api/sendFileApi"; + +export class SendFileData { + id: string; + fileName: string; + size: string; + sizeName: string; + + constructor(data?: SendFileApi) { + if (data == null) { + return; + } + + this.id = data.id; + this.fileName = data.fileName; + this.size = data.size; + this.sizeName = data.sizeName; + } +} diff --git a/jslib/common/src/models/data/sendTextData.ts b/jslib/common/src/models/data/sendTextData.ts new file mode 100644 index 00000000..fedf2ed6 --- /dev/null +++ b/jslib/common/src/models/data/sendTextData.ts @@ -0,0 +1,15 @@ +import { SendTextApi } from "../api/sendTextApi"; + +export class SendTextData { + text: string; + hidden: boolean; + + constructor(data?: SendTextApi) { + if (data == null) { + return; + } + + this.text = data.text; + this.hidden = data.hidden; + } +} diff --git a/jslib/common/src/models/domain/account.ts b/jslib/common/src/models/domain/account.ts new file mode 100644 index 00000000..e03f9be3 --- /dev/null +++ b/jslib/common/src/models/domain/account.ts @@ -0,0 +1,175 @@ +import { AuthenticationStatus } from "../../enums/authenticationStatus"; +import { KdfType } from "../../enums/kdfType"; +import { UriMatchType } from "../../enums/uriMatchType"; +import { CipherData } from "../data/cipherData"; +import { CollectionData } from "../data/collectionData"; +import { EventData } from "../data/eventData"; +import { FolderData } from "../data/folderData"; +import { OrganizationData } from "../data/organizationData"; +import { PolicyData } from "../data/policyData"; +import { ProviderData } from "../data/providerData"; +import { SendData } from "../data/sendData"; +import { CipherView } from "../view/cipherView"; +import { CollectionView } from "../view/collectionView"; +import { FolderView } from "../view/folderView"; +import { SendView } from "../view/sendView"; + +import { EncString } from "./encString"; +import { EnvironmentUrls } from "./environmentUrls"; +import { GeneratedPasswordHistory } from "./generatedPasswordHistory"; +import { Policy } from "./policy"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncryptionPair { + encrypted?: TEncrypted; + decrypted?: TDecrypted; +} + +export class DataEncryptionPair { + encrypted?: { [id: string]: TEncrypted }; + decrypted?: TDecrypted[]; +} + +export class AccountData { + ciphers?: DataEncryptionPair = new DataEncryptionPair< + CipherData, + CipherView + >(); + folders?: DataEncryptionPair = new DataEncryptionPair< + FolderData, + FolderView + >(); + localData?: any; + sends?: DataEncryptionPair = new DataEncryptionPair(); + collections?: DataEncryptionPair = new DataEncryptionPair< + CollectionData, + CollectionView + >(); + policies?: DataEncryptionPair = new DataEncryptionPair(); + passwordGenerationHistory?: EncryptionPair< + GeneratedPasswordHistory[], + GeneratedPasswordHistory[] + > = new EncryptionPair(); + addEditCipherInfo?: any; + eventCollection?: EventData[]; + organizations?: { [id: string]: OrganizationData }; + providers?: { [id: string]: ProviderData }; +} + +export class AccountKeys { + cryptoMasterKey?: SymmetricCryptoKey; + cryptoMasterKeyAuto?: string; + cryptoMasterKeyB64?: string; + cryptoMasterKeyBiometric?: string; + cryptoSymmetricKey?: EncryptionPair = new EncryptionPair< + string, + SymmetricCryptoKey + >(); + organizationKeys?: EncryptionPair> = new EncryptionPair< + any, + Map + >(); + providerKeys?: EncryptionPair> = new EncryptionPair< + any, + Map + >(); + privateKey?: EncryptionPair = new EncryptionPair(); + legacyEtmKey?: SymmetricCryptoKey; + publicKey?: ArrayBuffer; + apiKeyClientSecret?: string; +} + +export class AccountProfile { + apiKeyClientId?: string; + authenticationStatus?: AuthenticationStatus; + convertAccountToKeyConnector?: boolean; + email?: string; + emailVerified?: boolean; + entityId?: string; + entityType?: string; + everBeenUnlocked?: boolean; + forcePasswordReset?: boolean; + hasPremiumPersonally?: boolean; + lastSync?: string; + userId?: string; + usesKeyConnector?: boolean; + keyHash?: string; + kdfIterations?: number; + kdfType?: KdfType; +} + +export class AccountSettings { + autoConfirmFingerPrints?: boolean; + autoFillOnPageLoadDefault?: boolean; + biometricLocked?: boolean; + biometricUnlock?: boolean; + clearClipboard?: number; + collapsedGroupings?: string[]; + defaultUriMatch?: UriMatchType; + disableAddLoginNotification?: boolean; + disableAutoBiometricsPrompt?: boolean; + disableAutoTotpCopy?: boolean; + disableBadgeCounter?: boolean; + disableChangedPasswordNotification?: boolean; + disableContextMenuItem?: boolean; + disableGa?: boolean; + dontShowCardsCurrentTab?: boolean; + dontShowIdentitiesCurrentTab?: boolean; + enableAlwaysOnTop?: boolean; + enableAutoFillOnPageLoad?: boolean; + enableBiometric?: boolean; + enableFullWidth?: boolean; + enableGravitars?: boolean; + environmentUrls: EnvironmentUrls = new EnvironmentUrls(); + equivalentDomains?: any; + minimizeOnCopyToClipboard?: boolean; + neverDomains?: { [id: string]: any }; + passwordGenerationOptions?: any; + usernameGenerationOptions?: any; + generatorOptions?: any; + pinProtected?: EncryptionPair = new EncryptionPair(); + protectedPin?: string; + settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly + vaultTimeout?: number; + vaultTimeoutAction?: string = "lock"; +} + +export class AccountTokens { + accessToken?: string; + decodedToken?: any; + refreshToken?: string; + securityStamp?: string; +} + +export class Account { + data?: AccountData = new AccountData(); + keys?: AccountKeys = new AccountKeys(); + profile?: AccountProfile = new AccountProfile(); + settings?: AccountSettings = new AccountSettings(); + tokens?: AccountTokens = new AccountTokens(); + + constructor(init: Partial) { + Object.assign(this, { + data: { + ...new AccountData(), + ...init?.data, + }, + keys: { + ...new AccountKeys(), + ...init?.keys, + }, + profile: { + ...new AccountProfile(), + ...init?.profile, + }, + settings: { + ...new AccountSettings(), + ...init?.settings, + }, + tokens: { + ...new AccountTokens(), + ...init?.tokens, + }, + }); + } +} diff --git a/jslib/common/src/models/domain/attachment.ts b/jslib/common/src/models/domain/attachment.ts new file mode 100644 index 00000000..cbfffdcf --- /dev/null +++ b/jslib/common/src/models/domain/attachment.ts @@ -0,0 +1,87 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { Utils } from "../../misc/utils"; +import { AttachmentData } from "../data/attachmentData"; +import { AttachmentView } from "../view/attachmentView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Attachment extends Domain { + id: string; + url: string; + size: string; + sizeName: string; // Readable size, ex: "4.2 KB" or "1.43 GB" + key: EncString; + fileName: EncString; + + constructor(obj?: AttachmentData) { + super(); + if (obj == null) { + return; + } + + this.size = obj.size; + this.buildDomainModel( + this, + obj, + { + id: null, + url: null, + sizeName: null, + fileName: null, + key: null, + }, + ["id", "url", "sizeName"] + ); + } + + async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new AttachmentView(this), + { + fileName: null, + }, + orgId, + encKey + ); + + if (this.key != null) { + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + const orgKey = await cryptoService.getOrgKey(orgId); + const decValue = await cryptoService.decryptToBytes(this.key, orgKey ?? encKey); + view.key = new SymmetricCryptoKey(decValue); + } catch (e) { + // TODO: error? + } + } + + return view; + } + + toAttachmentData(): AttachmentData { + const a = new AttachmentData(); + a.size = this.size; + this.buildDataModel( + this, + a, + { + id: null, + url: null, + sizeName: null, + fileName: null, + key: null, + }, + ["id", "url", "sizeName"] + ); + return a; + } +} diff --git a/jslib/common/src/models/domain/authResult.ts b/jslib/common/src/models/domain/authResult.ts new file mode 100644 index 00000000..0781f872 --- /dev/null +++ b/jslib/common/src/models/domain/authResult.ts @@ -0,0 +1,17 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; +import { Utils } from "../../misc/utils"; + +export class AuthResult { + captchaSiteKey = ""; + resetMasterPassword = false; + forcePasswordReset = false; + twoFactorProviders: Map = null; + + get requiresCaptcha() { + return !Utils.isNullOrWhitespace(this.captchaSiteKey); + } + + get requiresTwoFactor() { + return this.twoFactorProviders != null; + } +} diff --git a/jslib/common/src/models/domain/card.ts b/jslib/common/src/models/domain/card.ts new file mode 100644 index 00000000..59f73f90 --- /dev/null +++ b/jslib/common/src/models/domain/card.ts @@ -0,0 +1,65 @@ +import { CardData } from "../data/cardData"; +import { CardView } from "../view/cardView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Card extends Domain { + cardholderName: EncString; + brand: EncString; + number: EncString; + expMonth: EncString; + expYear: EncString; + code: EncString; + + constructor(obj?: CardData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new CardView(), + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + orgId, + encKey + ); + } + + toCardData(): CardData { + const c = new CardData(); + this.buildDataModel(this, c, { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }); + return c; + } +} diff --git a/jslib/common/src/models/domain/cipher.ts b/jslib/common/src/models/domain/cipher.ts new file mode 100644 index 00000000..23f98686 --- /dev/null +++ b/jslib/common/src/models/domain/cipher.ts @@ -0,0 +1,238 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CipherData } from "../data/cipherData"; +import { CipherView } from "../view/cipherView"; + +import { Attachment } from "./attachment"; +import { Card } from "./card"; +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { Field } from "./field"; +import { Identity } from "./identity"; +import { Login } from "./login"; +import { Password } from "./password"; +import { SecureNote } from "./secureNote"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Cipher extends Domain { + id: string; + organizationId: string; + folderId: string; + name: EncString; + notes: EncString; + type: CipherType; + favorite: boolean; + organizationUseTotp: boolean; + edit: boolean; + viewPassword: boolean; + revisionDate: Date; + localData: any; + login: Login; + identity: Identity; + card: Card; + secureNote: SecureNote; + attachments: Attachment[]; + fields: Field[]; + passwordHistory: Password[]; + collectionIds: string[]; + deletedDate: Date; + reprompt: CipherRepromptType; + + constructor(obj?: CipherData, localData: any = null) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + userId: null, + organizationId: null, + folderId: null, + name: null, + notes: null, + }, + ["id", "userId", "organizationId", "folderId"] + ); + + this.type = obj.type; + this.favorite = obj.favorite; + this.organizationUseTotp = obj.organizationUseTotp; + this.edit = obj.edit; + if (obj.viewPassword != null) { + this.viewPassword = obj.viewPassword; + } else { + this.viewPassword = true; // Default for already synced Ciphers without viewPassword + } + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + this.collectionIds = obj.collectionIds; + this.localData = localData; + this.deletedDate = obj.deletedDate != null ? new Date(obj.deletedDate) : null; + this.reprompt = obj.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new Login(obj.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNote(obj.secureNote); + break; + case CipherType.Card: + this.card = new Card(obj.card); + break; + case CipherType.Identity: + this.identity = new Identity(obj.identity); + break; + default: + break; + } + + if (obj.attachments != null) { + this.attachments = obj.attachments.map((a) => new Attachment(a)); + } else { + this.attachments = null; + } + + if (obj.fields != null) { + this.fields = obj.fields.map((f) => new Field(f)); + } else { + this.fields = null; + } + + if (obj.passwordHistory != null) { + this.passwordHistory = obj.passwordHistory.map((ph) => new Password(ph)); + } else { + this.passwordHistory = null; + } + } + + async decrypt(encKey?: SymmetricCryptoKey): Promise { + const model = new CipherView(this); + + await this.decryptObj( + model, + { + name: null, + notes: null, + }, + this.organizationId, + encKey + ); + + switch (this.type) { + case CipherType.Login: + model.login = await this.login.decrypt(this.organizationId, encKey); + break; + case CipherType.SecureNote: + model.secureNote = await this.secureNote.decrypt(this.organizationId, encKey); + break; + case CipherType.Card: + model.card = await this.card.decrypt(this.organizationId, encKey); + break; + case CipherType.Identity: + model.identity = await this.identity.decrypt(this.organizationId, encKey); + break; + default: + break; + } + + const orgId = this.organizationId; + + if (this.attachments != null && this.attachments.length > 0) { + const attachments: any[] = []; + await this.attachments.reduce((promise, attachment) => { + return promise + .then(() => { + return attachment.decrypt(orgId, encKey); + }) + .then((decAttachment) => { + attachments.push(decAttachment); + }); + }, Promise.resolve()); + model.attachments = attachments; + } + + if (this.fields != null && this.fields.length > 0) { + const fields: any[] = []; + await this.fields.reduce((promise, field) => { + return promise + .then(() => { + return field.decrypt(orgId, encKey); + }) + .then((decField) => { + fields.push(decField); + }); + }, Promise.resolve()); + model.fields = fields; + } + + if (this.passwordHistory != null && this.passwordHistory.length > 0) { + const passwordHistory: any[] = []; + await this.passwordHistory.reduce((promise, ph) => { + return promise + .then(() => { + return ph.decrypt(orgId, encKey); + }) + .then((decPh) => { + passwordHistory.push(decPh); + }); + }, Promise.resolve()); + model.passwordHistory = passwordHistory; + } + + return model; + } + + toCipherData(userId: string): CipherData { + const c = new CipherData(); + c.id = this.id; + c.organizationId = this.organizationId; + c.folderId = this.folderId; + c.userId = this.organizationId != null ? userId : null; + c.edit = this.edit; + c.viewPassword = this.viewPassword; + c.organizationUseTotp = this.organizationUseTotp; + c.favorite = this.favorite; + c.revisionDate = this.revisionDate != null ? this.revisionDate.toISOString() : null; + c.type = this.type; + c.collectionIds = this.collectionIds; + c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null; + c.reprompt = this.reprompt; + + this.buildDataModel(this, c, { + name: null, + notes: null, + }); + + switch (c.type) { + case CipherType.Login: + c.login = this.login.toLoginData(); + break; + case CipherType.SecureNote: + c.secureNote = this.secureNote.toSecureNoteData(); + break; + case CipherType.Card: + c.card = this.card.toCardData(); + break; + case CipherType.Identity: + c.identity = this.identity.toIdentityData(); + break; + default: + break; + } + + if (this.fields != null) { + c.fields = this.fields.map((f) => f.toFieldData()); + } + if (this.attachments != null) { + c.attachments = this.attachments.map((a) => a.toAttachmentData()); + } + if (this.passwordHistory != null) { + c.passwordHistory = this.passwordHistory.map((ph) => ph.toPasswordHistoryData()); + } + return c; + } +} diff --git a/jslib/common/src/models/domain/collection.ts b/jslib/common/src/models/domain/collection.ts new file mode 100644 index 00000000..6f737cd4 --- /dev/null +++ b/jslib/common/src/models/domain/collection.ts @@ -0,0 +1,45 @@ +import { CollectionData } from "../data/collectionData"; +import { CollectionView } from "../view/collectionView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; + +export class Collection extends Domain { + id: string; + organizationId: string; + name: EncString; + externalId: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(obj?: CollectionData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + organizationId: null, + name: null, + externalId: null, + readOnly: null, + hidePasswords: null, + }, + ["id", "organizationId", "externalId", "readOnly", "hidePasswords"] + ); + } + + decrypt(): Promise { + return this.decryptObj( + new CollectionView(this), + { + name: null, + }, + this.organizationId + ); + } +} diff --git a/jslib/common/src/models/domain/decryptParameters.ts b/jslib/common/src/models/domain/decryptParameters.ts new file mode 100644 index 00000000..09a4c1d8 --- /dev/null +++ b/jslib/common/src/models/domain/decryptParameters.ts @@ -0,0 +1,8 @@ +export class DecryptParameters { + encKey: T; + data: T; + iv: T; + macKey: T; + mac: T; + macData: T; +} diff --git a/jslib/common/src/models/domain/domainBase.ts b/jslib/common/src/models/domain/domainBase.ts new file mode 100644 index 00000000..0c9e5ad6 --- /dev/null +++ b/jslib/common/src/models/domain/domainBase.ts @@ -0,0 +1,82 @@ +import { View } from "../view/view"; + +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export default class Domain { + protected buildDomainModel( + domain: D, + dataObj: any, + map: any, + notEncList: any[] = [] + ) { + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + const objProp = dataObj[map[prop] || prop]; + if (notEncList.indexOf(prop) > -1) { + (domain as any)[prop] = objProp ? objProp : null; + } else { + (domain as any)[prop] = objProp ? new EncString(objProp) : null; + } + } + } + protected buildDataModel( + domain: D, + dataObj: any, + map: any, + notEncStringList: any[] = [] + ) { + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + const objProp = (domain as any)[map[prop] || prop]; + if (notEncStringList.indexOf(prop) > -1) { + (dataObj as any)[prop] = objProp != null ? objProp : null; + } else { + (dataObj as any)[prop] = objProp != null ? (objProp as EncString).encryptedString : null; + } + } + } + + protected async decryptObj( + viewModel: T, + map: any, + orgId: string, + key: SymmetricCryptoKey = null + ): Promise { + const promises = []; + const self: any = this; + + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + (function (theProp) { + const p = Promise.resolve() + .then(() => { + const mapProp = map[theProp] || theProp; + if (self[mapProp]) { + return self[mapProp].decrypt(orgId, key); + } + return null; + }) + .then((val: any) => { + (viewModel as any)[theProp] = val; + }); + promises.push(p); + })(prop); + } + + await Promise.all(promises); + return viewModel; + } +} diff --git a/jslib/common/src/models/domain/encArrayBuffer.ts b/jslib/common/src/models/domain/encArrayBuffer.ts new file mode 100644 index 00000000..97f47c39 --- /dev/null +++ b/jslib/common/src/models/domain/encArrayBuffer.ts @@ -0,0 +1,3 @@ +export class EncArrayBuffer { + constructor(public buffer: ArrayBuffer) {} +} diff --git a/jslib/common/src/models/domain/encString.ts b/jslib/common/src/models/domain/encString.ts new file mode 100644 index 00000000..74ae8370 --- /dev/null +++ b/jslib/common/src/models/domain/encString.ts @@ -0,0 +1,122 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { EncryptionType } from "../../enums/encryptionType"; +import { Utils } from "../../misc/utils"; + +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncString { + encryptedString?: string; + encryptionType?: EncryptionType; + decryptedValue?: string; + data?: string; + iv?: string; + mac?: string; + + constructor( + encryptedStringOrType: string | EncryptionType, + data?: string, + iv?: string, + mac?: string + ) { + if (data != null) { + // data and header + const encType = encryptedStringOrType as EncryptionType; + + if (iv != null) { + this.encryptedString = encType + "." + iv + "|" + data; + } else { + this.encryptedString = encType + "." + data; + } + + // mac + if (mac != null) { + this.encryptedString += "|" + mac; + } + + this.encryptionType = encType; + this.data = data; + this.iv = iv; + this.mac = mac; + + return; + } + + this.encryptedString = encryptedStringOrType as string; + if (!this.encryptedString) { + return; + } + + const headerPieces = this.encryptedString.split("."); + let encPieces: string[] = null; + + if (headerPieces.length === 2) { + try { + this.encryptionType = parseInt(headerPieces[0], null); + encPieces = headerPieces[1].split("|"); + } catch (e) { + return; + } + } else { + encPieces = this.encryptedString.split("|"); + this.encryptionType = + encPieces.length === 3 + ? EncryptionType.AesCbc128_HmacSha256_B64 + : EncryptionType.AesCbc256_B64; + } + + switch (this.encryptionType) { + case EncryptionType.AesCbc128_HmacSha256_B64: + case EncryptionType.AesCbc256_HmacSha256_B64: + if (encPieces.length !== 3) { + return; + } + + this.iv = encPieces[0]; + this.data = encPieces[1]; + this.mac = encPieces[2]; + break; + case EncryptionType.AesCbc256_B64: + if (encPieces.length !== 2) { + return; + } + + this.iv = encPieces[0]; + this.data = encPieces[1]; + break; + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha1_B64: + if (encPieces.length !== 1) { + return; + } + + this.data = encPieces[0]; + break; + default: + return; + } + } + + async decrypt(orgId: string, key: SymmetricCryptoKey = null): Promise { + if (this.decryptedValue != null) { + return this.decryptedValue; + } + + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + if (key == null) { + key = await cryptoService.getOrgKey(orgId); + } + this.decryptedValue = await cryptoService.decryptToUtf8(this, key); + } catch (e) { + this.decryptedValue = "[error: cannot decrypt]"; + } + return this.decryptedValue; + } +} diff --git a/jslib/common/src/models/domain/encryptedObject.ts b/jslib/common/src/models/domain/encryptedObject.ts new file mode 100644 index 00000000..5ce93dbe --- /dev/null +++ b/jslib/common/src/models/domain/encryptedObject.ts @@ -0,0 +1,8 @@ +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class EncryptedObject { + iv: ArrayBuffer; + data: ArrayBuffer; + mac: ArrayBuffer; + key: SymmetricCryptoKey; +} diff --git a/jslib/common/src/models/domain/environmentUrls.ts b/jslib/common/src/models/domain/environmentUrls.ts new file mode 100644 index 00000000..d4fd173c --- /dev/null +++ b/jslib/common/src/models/domain/environmentUrls.ts @@ -0,0 +1,10 @@ +export class EnvironmentUrls { + base: string = null; + api: string = null; + identity: string = null; + icons: string = null; + notifications: string = null; + events: string = null; + webVault: string = null; + keyConnector: string = null; +} diff --git a/jslib/common/src/models/domain/field.ts b/jslib/common/src/models/domain/field.ts new file mode 100644 index 00000000..71dc615a --- /dev/null +++ b/jslib/common/src/models/domain/field.ts @@ -0,0 +1,62 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { FieldData } from "../data/fieldData"; +import { FieldView } from "../view/fieldView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Field extends Domain { + name: EncString; + value: EncString; + type: FieldType; + linkedId: LinkedIdType; + + constructor(obj?: FieldData) { + super(); + if (obj == null) { + return; + } + + this.type = obj.type; + this.linkedId = obj.linkedId; + this.buildDomainModel( + this, + obj, + { + name: null, + value: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new FieldView(this), + { + name: null, + value: null, + }, + orgId, + encKey + ); + } + + toFieldData(): FieldData { + const f = new FieldData(); + this.buildDataModel( + this, + f, + { + name: null, + value: null, + type: null, + linkedId: null, + }, + ["type", "linkedId"] + ); + return f; + } +} diff --git a/jslib/common/src/models/domain/folder.ts b/jslib/common/src/models/domain/folder.ts new file mode 100644 index 00000000..8596e94d --- /dev/null +++ b/jslib/common/src/models/domain/folder.ts @@ -0,0 +1,40 @@ +import { FolderData } from "../data/folderData"; +import { FolderView } from "../view/folderView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; + +export class Folder extends Domain { + id: string; + name: EncString; + revisionDate: Date; + + constructor(obj?: FolderData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + name: null, + }, + ["id"] + ); + + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + } + + decrypt(): Promise { + return this.decryptObj( + new FolderView(this), + { + name: null, + }, + null + ); + } +} diff --git a/jslib/common/src/models/domain/generatedPasswordHistory.ts b/jslib/common/src/models/domain/generatedPasswordHistory.ts new file mode 100644 index 00000000..b4cc9b22 --- /dev/null +++ b/jslib/common/src/models/domain/generatedPasswordHistory.ts @@ -0,0 +1,9 @@ +export class GeneratedPasswordHistory { + password: string; + date: number; + + constructor(password: string, date: number) { + this.password = password; + this.date = date; + } +} diff --git a/jslib/common/src/models/domain/globalState.ts b/jslib/common/src/models/domain/globalState.ts new file mode 100644 index 00000000..326ba11b --- /dev/null +++ b/jslib/common/src/models/domain/globalState.ts @@ -0,0 +1,40 @@ +import { StateVersion } from "../../enums/stateVersion"; +import { ThemeType } from "../../enums/themeType"; + +import { EnvironmentUrls } from "./environmentUrls"; +import { WindowState } from "./windowState"; + +export class GlobalState { + enableAlwaysOnTop?: boolean; + installedVersion?: string; + locale?: string = "en"; + organizationInvitation?: any; + ssoCodeVerifier?: string; + ssoOrganizationIdentifier?: string; + ssoState?: string; + rememberedEmail?: string; + theme?: ThemeType = ThemeType.System; + window?: WindowState = new WindowState(); + twoFactorToken?: string; + disableFavicon?: boolean; + biometricAwaitingAcceptance?: boolean; + biometricFingerprintValidated?: boolean; + vaultTimeout?: number; + vaultTimeoutAction?: string; + loginRedirect?: any; + mainWindowSize?: number; + enableBiometrics?: boolean; + biometricText?: string; + noAutoPromptBiometrics?: boolean; + noAutoPromptBiometricsText?: string; + stateVersion: StateVersion = StateVersion.One; + environmentUrls: EnvironmentUrls = new EnvironmentUrls(); + enableTray?: boolean; + enableMinimizeToTray?: boolean; + enableCloseToTray?: boolean; + enableStartToTray?: boolean; + openAtLogin?: boolean; + alwaysShowDock?: boolean; + enableBrowserIntegration?: boolean; + enableBrowserIntegrationFingerprint?: boolean; +} diff --git a/jslib/common/src/models/domain/identity.ts b/jslib/common/src/models/domain/identity.ts new file mode 100644 index 00000000..4af22849 --- /dev/null +++ b/jslib/common/src/models/domain/identity.ts @@ -0,0 +1,113 @@ +import { IdentityData } from "../data/identityData"; +import { IdentityView } from "../view/identityView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Identity extends Domain { + title: EncString; + firstName: EncString; + middleName: EncString; + lastName: EncString; + address1: EncString; + address2: EncString; + address3: EncString; + city: EncString; + state: EncString; + postalCode: EncString; + country: EncString; + company: EncString; + email: EncString; + phone: EncString; + ssn: EncString; + username: EncString; + passportNumber: EncString; + licenseNumber: EncString; + + constructor(obj?: IdentityData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new IdentityView(), + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + orgId, + encKey + ); + } + + toIdentityData(): IdentityData { + const i = new IdentityData(); + this.buildDataModel(this, i, { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }); + return i; + } +} diff --git a/jslib/common/src/models/domain/importResult.ts b/jslib/common/src/models/domain/importResult.ts new file mode 100644 index 00000000..695dbf9d --- /dev/null +++ b/jslib/common/src/models/domain/importResult.ts @@ -0,0 +1,14 @@ +import { CipherView } from "../view/cipherView"; +import { CollectionView } from "../view/collectionView"; +import { FolderView } from "../view/folderView"; + +export class ImportResult { + success = false; + missingPassword = false; + errorMessage: string; + ciphers: CipherView[] = []; + folders: FolderView[] = []; + folderRelationships: [number, number][] = []; + collections: CollectionView[] = []; + collectionRelationships: [number, number][] = []; +} diff --git a/jslib/common/src/models/domain/logInCredentials.ts b/jslib/common/src/models/domain/logInCredentials.ts new file mode 100644 index 00000000..c1e23610 --- /dev/null +++ b/jslib/common/src/models/domain/logInCredentials.ts @@ -0,0 +1,31 @@ +import { AuthenticationType } from "../../enums/authenticationType"; +import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequestTwoFactor"; + +export class PasswordLogInCredentials { + readonly type = AuthenticationType.Password; + + constructor( + public email: string, + public masterPassword: string, + public captchaToken?: string, + public twoFactor?: TokenRequestTwoFactor + ) {} +} + +export class SsoLogInCredentials { + readonly type = AuthenticationType.Sso; + + constructor( + public code: string, + public codeVerifier: string, + public redirectUrl: string, + public orgId: string, + public twoFactor?: TokenRequestTwoFactor + ) {} +} + +export class ApiLogInCredentials { + readonly type = AuthenticationType.Api; + + constructor(public clientId: string, public clientSecret: string) {} +} diff --git a/jslib/common/src/models/domain/login.ts b/jslib/common/src/models/domain/login.ts new file mode 100644 index 00000000..76ba4020 --- /dev/null +++ b/jslib/common/src/models/domain/login.ts @@ -0,0 +1,88 @@ +import { LoginData } from "../data/loginData"; +import { LoginView } from "../view/loginView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { LoginUri } from "./loginUri"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Login extends Domain { + uris: LoginUri[]; + username: EncString; + password: EncString; + passwordRevisionDate?: Date; + totp: EncString; + autofillOnPageLoad: boolean; + + constructor(obj?: LoginData) { + super(); + if (obj == null) { + return; + } + + this.passwordRevisionDate = + obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null; + this.autofillOnPageLoad = obj.autofillOnPageLoad; + this.buildDomainModel( + this, + obj, + { + username: null, + password: null, + totp: null, + }, + [] + ); + + if (obj.uris) { + this.uris = []; + obj.uris.forEach((u) => { + this.uris.push(new LoginUri(u)); + }); + } + } + + async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new LoginView(this), + { + username: null, + password: null, + totp: null, + }, + orgId, + encKey + ); + + if (this.uris != null) { + view.uris = []; + for (let i = 0; i < this.uris.length; i++) { + const uri = await this.uris[i].decrypt(orgId, encKey); + view.uris.push(uri); + } + } + + return view; + } + + toLoginData(): LoginData { + const l = new LoginData(); + l.passwordRevisionDate = + this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null; + l.autofillOnPageLoad = this.autofillOnPageLoad; + this.buildDataModel(this, l, { + username: null, + password: null, + totp: null, + }); + + if (this.uris != null && this.uris.length > 0) { + l.uris = []; + this.uris.forEach((u) => { + l.uris.push(u.toLoginUriData()); + }); + } + + return l; + } +} diff --git a/jslib/common/src/models/domain/loginUri.ts b/jslib/common/src/models/domain/loginUri.ts new file mode 100644 index 00000000..9bd78c65 --- /dev/null +++ b/jslib/common/src/models/domain/loginUri.ts @@ -0,0 +1,54 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { LoginUriData } from "../data/loginUriData"; +import { LoginUriView } from "../view/loginUriView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class LoginUri extends Domain { + uri: EncString; + match: UriMatchType; + + constructor(obj?: LoginUriData) { + super(); + if (obj == null) { + return; + } + + this.match = obj.match; + this.buildDomainModel( + this, + obj, + { + uri: null, + }, + [] + ); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new LoginUriView(this), + { + uri: null, + }, + orgId, + encKey + ); + } + + toLoginUriData(): LoginUriData { + const u = new LoginUriData(); + this.buildDataModel( + this, + u, + { + uri: null, + match: null, + }, + ["match"] + ); + return u; + } +} diff --git a/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts b/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts new file mode 100644 index 00000000..2207d00c --- /dev/null +++ b/jslib/common/src/models/domain/masterPasswordPolicyOptions.ts @@ -0,0 +1,10 @@ +import Domain from "./domainBase"; + +export class MasterPasswordPolicyOptions extends Domain { + minComplexity = 0; + minLength = 0; + requireUpper = false; + requireLower = false; + requireNumbers = false; + requireSpecial = false; +} diff --git a/jslib/common/src/models/domain/organization.ts b/jslib/common/src/models/domain/organization.ts new file mode 100644 index 00000000..8a682212 --- /dev/null +++ b/jslib/common/src/models/domain/organization.ts @@ -0,0 +1,184 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; +import { OrganizationData } from "../data/organizationData"; + +export class Organization { + id: string; + name: string; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + hasPublicAndPrivateKeys: boolean; + providerId: string; + providerName: string; + isProviderUser: boolean; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(obj?: OrganizationData) { + if (obj == null) { + return; + } + + this.id = obj.id; + this.name = obj.name; + this.status = obj.status; + this.type = obj.type; + this.enabled = obj.enabled; + this.usePolicies = obj.usePolicies; + this.useGroups = obj.useGroups; + this.useDirectory = obj.useDirectory; + this.useEvents = obj.useEvents; + this.useTotp = obj.useTotp; + this.use2fa = obj.use2fa; + this.useApi = obj.useApi; + this.useSso = obj.useSso; + this.useKeyConnector = obj.useKeyConnector; + this.useResetPassword = obj.useResetPassword; + this.selfHost = obj.selfHost; + this.usersGetPremium = obj.usersGetPremium; + this.seats = obj.seats; + this.maxCollections = obj.maxCollections; + this.maxStorageGb = obj.maxStorageGb; + this.ssoBound = obj.ssoBound; + this.identifier = obj.identifier; + this.permissions = obj.permissions; + this.resetPasswordEnrolled = obj.resetPasswordEnrolled; + this.userId = obj.userId; + this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys; + this.providerId = obj.providerId; + this.providerName = obj.providerName; + this.isProviderUser = obj.isProviderUser; + this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName; + this.familySponsorshipAvailable = obj.familySponsorshipAvailable; + this.planProductType = obj.planProductType; + this.keyConnectorEnabled = obj.keyConnectorEnabled; + this.keyConnectorUrl = obj.keyConnectorUrl; + } + + get canAccess() { + if (this.type === OrganizationUserType.Owner) { + return true; + } + return this.enabled && this.status === OrganizationUserStatusType.Confirmed; + } + + get isManager() { + return ( + this.type === OrganizationUserType.Manager || + this.type === OrganizationUserType.Owner || + this.type === OrganizationUserType.Admin + ); + } + + get isAdmin() { + return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin; + } + + get isOwner() { + return this.type === OrganizationUserType.Owner || this.isProviderUser; + } + + get canAccessEventLogs() { + return this.isAdmin || this.permissions.accessEventLogs; + } + + get canAccessImportExport() { + return this.isAdmin || this.permissions.accessImportExport; + } + + get canAccessReports() { + return this.isAdmin || this.permissions.accessReports; + } + + get canCreateNewCollections() { + return ( + this.isManager || + (this.permissions.createNewCollections ?? this.permissions.manageAllCollections) + ); + } + + get canEditAnyCollection() { + return ( + this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections) + ); + } + + get canDeleteAnyCollection() { + return ( + this.isAdmin || + (this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections) + ); + } + + get canViewAllCollections() { + return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection; + } + + get canEditAssignedCollections() { + return ( + this.isManager || + (this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections) + ); + } + + get canDeleteAssignedCollections() { + return ( + this.isManager || + (this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections) + ); + } + + get canViewAssignedCollections() { + return this.canDeleteAssignedCollections || this.canEditAssignedCollections; + } + + get canManageGroups() { + return this.isAdmin || this.permissions.manageGroups; + } + + get canManageSso() { + return this.isAdmin || this.permissions.manageSso; + } + + get canManagePolicies() { + return this.isAdmin || this.permissions.managePolicies; + } + + get canManageUsers() { + return this.isAdmin || this.permissions.manageUsers; + } + + get canManageUsersPassword() { + return this.isAdmin || this.permissions.manageResetPassword; + } + + get isExemptFromPolicies() { + return this.canManagePolicies; + } +} diff --git a/jslib/common/src/models/domain/password.ts b/jslib/common/src/models/domain/password.ts new file mode 100644 index 00000000..ae47b18d --- /dev/null +++ b/jslib/common/src/models/domain/password.ts @@ -0,0 +1,43 @@ +import { PasswordHistoryData } from "../data/passwordHistoryData"; +import { PasswordHistoryView } from "../view/passwordHistoryView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class Password extends Domain { + password: EncString; + lastUsedDate: Date; + + constructor(obj?: PasswordHistoryData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel(this, obj, { + password: null, + }); + this.lastUsedDate = new Date(obj.lastUsedDate); + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return this.decryptObj( + new PasswordHistoryView(this), + { + password: null, + }, + orgId, + encKey + ); + } + + toPasswordHistoryData(): PasswordHistoryData { + const ph = new PasswordHistoryData(); + ph.lastUsedDate = this.lastUsedDate.toISOString(); + this.buildDataModel(this, ph, { + password: null, + }); + return ph; + } +} diff --git a/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts b/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts new file mode 100644 index 00000000..19072a9f --- /dev/null +++ b/jslib/common/src/models/domain/passwordGeneratorPolicyOptions.ts @@ -0,0 +1,31 @@ +import Domain from "./domainBase"; + +export class PasswordGeneratorPolicyOptions extends Domain { + defaultType = ""; + minLength = 0; + useUppercase = false; + useLowercase = false; + useNumbers = false; + numberCount = 0; + useSpecial = false; + specialCount = 0; + minNumberWords = 0; + capitalize = false; + includeNumber = false; + + inEffect() { + return ( + this.defaultType !== "" || + this.minLength > 0 || + this.numberCount > 0 || + this.specialCount > 0 || + this.useUppercase || + this.useLowercase || + this.useNumbers || + this.useSpecial || + this.minNumberWords > 0 || + this.capitalize || + this.includeNumber + ); + } +} diff --git a/jslib/common/src/models/domain/policy.ts b/jslib/common/src/models/domain/policy.ts new file mode 100644 index 00000000..c4a113cc --- /dev/null +++ b/jslib/common/src/models/domain/policy.ts @@ -0,0 +1,25 @@ +import { PolicyType } from "../../enums/policyType"; +import { PolicyData } from "../data/policyData"; + +import Domain from "./domainBase"; + +export class Policy extends Domain { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(obj?: PolicyData) { + super(); + if (obj == null) { + return; + } + + this.id = obj.id; + this.organizationId = obj.organizationId; + this.type = obj.type; + this.data = obj.data; + this.enabled = obj.enabled; + } +} diff --git a/jslib/common/src/models/domain/provider.ts b/jslib/common/src/models/domain/provider.ts new file mode 100644 index 00000000..6e14340b --- /dev/null +++ b/jslib/common/src/models/domain/provider.ts @@ -0,0 +1,50 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { ProviderData } from "../data/providerData"; + +export class Provider { + id: string; + name: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + userId: string; + useEvents: boolean; + + constructor(obj?: ProviderData) { + if (obj == null) { + return; + } + + this.id = obj.id; + this.name = obj.name; + this.status = obj.status; + this.type = obj.type; + this.enabled = obj.enabled; + this.userId = obj.userId; + this.useEvents = obj.useEvents; + } + + get canAccess() { + if (this.isProviderAdmin) { + return true; + } + return this.enabled && this.status === ProviderUserStatusType.Confirmed; + } + + get canCreateOrganizations() { + return this.enabled && this.isProviderAdmin; + } + + get canManageUsers() { + return this.isProviderAdmin; + } + + get canAccessEventLogs() { + return this.isProviderAdmin; + } + + get isProviderAdmin() { + return this.type === ProviderUserType.ProviderAdmin; + } +} diff --git a/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts b/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts new file mode 100644 index 00000000..2ee11a5c --- /dev/null +++ b/jslib/common/src/models/domain/resetPasswordPolicyOptions.ts @@ -0,0 +1,5 @@ +import Domain from "./domainBase"; + +export class ResetPasswordPolicyOptions extends Domain { + autoEnrollEnabled = false; +} diff --git a/jslib/common/src/models/domain/secureNote.ts b/jslib/common/src/models/domain/secureNote.ts new file mode 100644 index 00000000..8bde3164 --- /dev/null +++ b/jslib/common/src/models/domain/secureNote.ts @@ -0,0 +1,29 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNoteData } from "../data/secureNoteData"; +import { SecureNoteView } from "../view/secureNoteView"; + +import Domain from "./domainBase"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SecureNote extends Domain { + type: SecureNoteType; + + constructor(obj?: SecureNoteData) { + super(); + if (obj == null) { + return; + } + + this.type = obj.type; + } + + decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise { + return Promise.resolve(new SecureNoteView(this)); + } + + toSecureNoteData(): SecureNoteData { + const n = new SecureNoteData(); + n.type = this.type; + return n; + } +} diff --git a/jslib/common/src/models/domain/send.ts b/jslib/common/src/models/domain/send.ts new file mode 100644 index 00000000..c2e769ec --- /dev/null +++ b/jslib/common/src/models/domain/send.ts @@ -0,0 +1,114 @@ +import { CryptoService } from "../../abstractions/crypto.service"; +import { SendType } from "../../enums/sendType"; +import { Utils } from "../../misc/utils"; +import { SendData } from "../data/sendData"; +import { SendView } from "../view/sendView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SendFile } from "./sendFile"; +import { SendText } from "./sendText"; + +export class Send extends Domain { + id: string; + accessId: string; + userId: string; + type: SendType; + name: EncString; + notes: EncString; + file: SendFile; + text: SendText; + key: EncString; + maxAccessCount?: number; + accessCount: number; + revisionDate: Date; + expirationDate: Date; + deletionDate: Date; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(obj?: SendData) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + accessId: null, + userId: null, + name: null, + notes: null, + key: null, + }, + ["id", "accessId", "userId"] + ); + + this.type = obj.type; + this.maxAccessCount = obj.maxAccessCount; + this.accessCount = obj.accessCount; + this.password = obj.password; + this.disabled = obj.disabled; + this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; + this.deletionDate = obj.deletionDate != null ? new Date(obj.deletionDate) : null; + this.expirationDate = obj.expirationDate != null ? new Date(obj.expirationDate) : null; + this.hideEmail = obj.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendText(obj.text); + break; + case SendType.File: + this.file = new SendFile(obj.file); + break; + default: + break; + } + } + + async decrypt(): Promise { + const model = new SendView(this); + + let cryptoService: CryptoService; + const containerService = (Utils.global as any).bitwardenContainerService; + if (containerService) { + cryptoService = containerService.getCryptoService(); + } else { + throw new Error("global bitwardenContainerService not initialized."); + } + + try { + model.key = await cryptoService.decryptToBytes(this.key, null); + model.cryptoKey = await cryptoService.makeSendKey(model.key); + } catch (e) { + // TODO: error? + } + + await this.decryptObj( + model, + { + name: null, + notes: null, + }, + null, + model.cryptoKey + ); + + switch (this.type) { + case SendType.File: + model.file = await this.file.decrypt(model.cryptoKey); + break; + case SendType.Text: + model.text = await this.text.decrypt(model.cryptoKey); + break; + default: + break; + } + + return model; + } +} diff --git a/jslib/common/src/models/domain/sendAccess.ts b/jslib/common/src/models/domain/sendAccess.ts new file mode 100644 index 00000000..dab68cc6 --- /dev/null +++ b/jslib/common/src/models/domain/sendAccess.ts @@ -0,0 +1,77 @@ +import { SendType } from "../../enums/sendType"; +import { SendAccessResponse } from "../response/sendAccessResponse"; +import { SendAccessView } from "../view/sendAccessView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SendFile } from "./sendFile"; +import { SendText } from "./sendText"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendAccess extends Domain { + id: string; + type: SendType; + name: EncString; + file: SendFile; + text: SendText; + expirationDate: Date; + creatorIdentifier: string; + + constructor(obj?: SendAccessResponse) { + super(); + if (obj == null) { + return; + } + + this.buildDomainModel( + this, + obj, + { + id: null, + name: null, + expirationDate: null, + creatorIdentifier: null, + }, + ["id", "expirationDate", "creatorIdentifier"] + ); + + this.type = obj.type; + + switch (this.type) { + case SendType.Text: + this.text = new SendText(obj.text); + break; + case SendType.File: + this.file = new SendFile(obj.file); + break; + default: + break; + } + } + + async decrypt(key: SymmetricCryptoKey): Promise { + const model = new SendAccessView(this); + + await this.decryptObj( + model, + { + name: null, + }, + null, + key + ); + + switch (this.type) { + case SendType.File: + model.file = await this.file.decrypt(key); + break; + case SendType.Text: + model.text = await this.text.decrypt(key); + break; + default: + break; + } + + return model; + } +} diff --git a/jslib/common/src/models/domain/sendFile.ts b/jslib/common/src/models/domain/sendFile.ts new file mode 100644 index 00000000..17799407 --- /dev/null +++ b/jslib/common/src/models/domain/sendFile.ts @@ -0,0 +1,44 @@ +import { SendFileData } from "../data/sendFileData"; +import { SendFileView } from "../view/sendFileView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendFile extends Domain { + id: string; + size: string; + sizeName: string; + fileName: EncString; + + constructor(obj?: SendFileData) { + super(); + if (obj == null) { + return; + } + + this.size = obj.size; + this.buildDomainModel( + this, + obj, + { + id: null, + sizeName: null, + fileName: null, + }, + ["id", "sizeName"] + ); + } + + async decrypt(key: SymmetricCryptoKey): Promise { + const view = await this.decryptObj( + new SendFileView(this), + { + fileName: null, + }, + null, + key + ); + return view; + } +} diff --git a/jslib/common/src/models/domain/sendText.ts b/jslib/common/src/models/domain/sendText.ts new file mode 100644 index 00000000..523e5ee5 --- /dev/null +++ b/jslib/common/src/models/domain/sendText.ts @@ -0,0 +1,39 @@ +import { SendTextData } from "../data/sendTextData"; +import { SendTextView } from "../view/sendTextView"; + +import Domain from "./domainBase"; +import { EncString } from "./encString"; +import { SymmetricCryptoKey } from "./symmetricCryptoKey"; + +export class SendText extends Domain { + text: EncString; + hidden: boolean; + + constructor(obj?: SendTextData) { + super(); + if (obj == null) { + return; + } + + this.hidden = obj.hidden; + this.buildDomainModel( + this, + obj, + { + text: null, + }, + [] + ); + } + + decrypt(key: SymmetricCryptoKey): Promise { + return this.decryptObj( + new SendTextView(this), + { + text: null, + }, + null, + key + ); + } +} diff --git a/jslib/common/src/models/domain/sortedCiphersCache.ts b/jslib/common/src/models/domain/sortedCiphersCache.ts new file mode 100644 index 00000000..8c32744e --- /dev/null +++ b/jslib/common/src/models/domain/sortedCiphersCache.ts @@ -0,0 +1,87 @@ +import { CipherView } from "../view/cipherView"; + +const CacheTTL = 3000; + +export class SortedCiphersCache { + private readonly sortedCiphersByUrl: Map = new Map(); + private readonly timeouts: Map = new Map(); + + constructor(private readonly comparator: (a: CipherView, b: CipherView) => number) {} + + isCached(url: string) { + return this.sortedCiphersByUrl.has(url); + } + + addCiphers(url: string, ciphers: CipherView[]) { + ciphers.sort(this.comparator); + this.sortedCiphersByUrl.set(url, new Ciphers(ciphers)); + this.resetTimer(url); + } + + getLastUsed(url: string) { + this.resetTimer(url); + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastUsed() : null; + } + + getLastLaunched(url: string) { + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastLaunched() : null; + } + + getNext(url: string) { + this.resetTimer(url); + return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getNext() : null; + } + + updateLastUsedIndex(url: string) { + if (this.isCached(url)) { + this.sortedCiphersByUrl.get(url).updateLastUsedIndex(); + } + } + + clear() { + this.sortedCiphersByUrl.clear(); + this.timeouts.clear(); + } + + private resetTimer(url: string) { + clearTimeout(this.timeouts.get(url)); + this.timeouts.set( + url, + setTimeout(() => { + this.sortedCiphersByUrl.delete(url); + this.timeouts.delete(url); + }, CacheTTL) + ); + } +} + +class Ciphers { + lastUsedIndex = -1; + + constructor(private readonly ciphers: CipherView[]) {} + + getLastUsed() { + this.lastUsedIndex = Math.max(this.lastUsedIndex, 0); + return this.ciphers[this.lastUsedIndex]; + } + + getLastLaunched() { + const usedCiphers = this.ciphers.filter((cipher) => cipher.localData?.lastLaunched); + const sortedCiphers = usedCiphers.sort( + (x, y) => y.localData.lastLaunched.valueOf() - x.localData.lastLaunched.valueOf() + ); + return sortedCiphers[0]; + } + + getNextIndex() { + return (this.lastUsedIndex + 1) % this.ciphers.length; + } + + getNext() { + return this.ciphers[this.getNextIndex()]; + } + + updateLastUsedIndex() { + this.lastUsedIndex = this.getNextIndex(); + } +} diff --git a/jslib/common/src/models/domain/state.ts b/jslib/common/src/models/domain/state.ts new file mode 100644 index 00000000..f5a2c046 --- /dev/null +++ b/jslib/common/src/models/domain/state.ts @@ -0,0 +1,17 @@ +import { Account } from "./account"; +import { GlobalState } from "./globalState"; + +export class State< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + accounts: { [userId: string]: TAccount } = {}; + globals: TGlobalState; + activeUserId: string; + authenticatedAccounts: string[] = []; + accountActivity: { [userId: string]: number } = {}; + + constructor(globals: TGlobalState) { + this.globals = globals; + } +} diff --git a/jslib/common/src/models/domain/storageOptions.ts b/jslib/common/src/models/domain/storageOptions.ts new file mode 100644 index 00000000..2db7e0cc --- /dev/null +++ b/jslib/common/src/models/domain/storageOptions.ts @@ -0,0 +1,10 @@ +import { HtmlStorageLocation } from "../../enums/htmlStorageLocation"; +import { StorageLocation } from "../../enums/storageLocation"; + +export type StorageOptions = { + storageLocation?: StorageLocation; + useSecureStorage?: boolean; + userId?: string; + htmlStorageLocation?: HtmlStorageLocation; + keySuffix?: string; +}; diff --git a/jslib/common/src/models/domain/symmetricCryptoKey.ts b/jslib/common/src/models/domain/symmetricCryptoKey.ts new file mode 100644 index 00000000..a58dc6fd --- /dev/null +++ b/jslib/common/src/models/domain/symmetricCryptoKey.ts @@ -0,0 +1,57 @@ +import { EncryptionType } from "../../enums/encryptionType"; +import { Utils } from "../../misc/utils"; + +export class SymmetricCryptoKey { + key: ArrayBuffer; + encKey?: ArrayBuffer; + macKey?: ArrayBuffer; + encType: EncryptionType; + + keyB64: string; + encKeyB64: string; + macKeyB64: string; + + meta: any; + + constructor(key: ArrayBuffer, encType?: EncryptionType) { + if (key == null) { + throw new Error("Must provide key"); + } + + if (encType == null) { + if (key.byteLength === 32) { + encType = EncryptionType.AesCbc256_B64; + } else if (key.byteLength === 64) { + encType = EncryptionType.AesCbc256_HmacSha256_B64; + } else { + throw new Error("Unable to determine encType."); + } + } + + this.key = key; + this.encType = encType; + + if (encType === EncryptionType.AesCbc256_B64 && key.byteLength === 32) { + this.encKey = key; + this.macKey = null; + } else if (encType === EncryptionType.AesCbc128_HmacSha256_B64 && key.byteLength === 32) { + this.encKey = key.slice(0, 16); + this.macKey = key.slice(16, 32); + } else if (encType === EncryptionType.AesCbc256_HmacSha256_B64 && key.byteLength === 64) { + this.encKey = key.slice(0, 32); + this.macKey = key.slice(32, 64); + } else { + throw new Error("Unsupported encType/key length."); + } + + if (this.key != null) { + this.keyB64 = Utils.fromBufferToB64(this.key); + } + if (this.encKey != null) { + this.encKeyB64 = Utils.fromBufferToB64(this.encKey); + } + if (this.macKey != null) { + this.macKeyB64 = Utils.fromBufferToB64(this.macKey); + } + } +} diff --git a/jslib/common/src/models/domain/treeNode.ts b/jslib/common/src/models/domain/treeNode.ts new file mode 100644 index 00000000..6af973a5 --- /dev/null +++ b/jslib/common/src/models/domain/treeNode.ts @@ -0,0 +1,16 @@ +export class TreeNode { + parent: T; + node: T; + children: TreeNode[] = []; + + constructor(node: T, name: string, parent: T) { + this.parent = parent; + this.node = node; + this.node.name = name; + } +} + +export interface ITreeNodeObject { + id: string; + name: string; +} diff --git a/jslib/common/src/models/domain/windowState.ts b/jslib/common/src/models/domain/windowState.ts new file mode 100644 index 00000000..cb260d8b --- /dev/null +++ b/jslib/common/src/models/domain/windowState.ts @@ -0,0 +1,10 @@ +export class WindowState { + width?: number; + height?: number; + isMaximized?: boolean; + // TODO: displayBounds is an Electron.Rectangle. + // We need to establish some kind of client-specific global state, similiar to the way we already extend a base Account. + displayBounds: any; + x?: number; + y?: number; +} diff --git a/jslib/common/src/models/export/card.ts b/jslib/common/src/models/export/card.ts new file mode 100644 index 00000000..b320ec8b --- /dev/null +++ b/jslib/common/src/models/export/card.ts @@ -0,0 +1,65 @@ +import { Card as CardDomain } from "../domain/card"; +import { EncString } from "../domain/encString"; +import { CardView } from "../view/cardView"; + +export class Card { + static template(): Card { + const req = new Card(); + req.cardholderName = "John Doe"; + req.brand = "visa"; + req.number = "4242424242424242"; + req.expMonth = "04"; + req.expYear = "2023"; + req.code = "123"; + return req; + } + + static toView(req: Card, view = new CardView()) { + view.cardholderName = req.cardholderName; + view.brand = req.brand; + view.number = req.number; + view.expMonth = req.expMonth; + view.expYear = req.expYear; + view.code = req.code; + return view; + } + + static toDomain(req: Card, domain = new CardDomain()) { + domain.cardholderName = req.cardholderName != null ? new EncString(req.cardholderName) : null; + domain.brand = req.brand != null ? new EncString(req.brand) : null; + domain.number = req.number != null ? new EncString(req.number) : null; + domain.expMonth = req.expMonth != null ? new EncString(req.expMonth) : null; + domain.expYear = req.expYear != null ? new EncString(req.expYear) : null; + domain.code = req.code != null ? new EncString(req.code) : null; + return domain; + } + + cardholderName: string; + brand: string; + number: string; + expMonth: string; + expYear: string; + code: string; + + constructor(o?: CardView | CardDomain) { + if (o == null) { + return; + } + + if (o instanceof CardView) { + this.cardholderName = o.cardholderName; + this.brand = o.brand; + this.number = o.number; + this.expMonth = o.expMonth; + this.expYear = o.expYear; + this.code = o.code; + } else { + this.cardholderName = o.cardholderName?.encryptedString; + this.brand = o.brand?.encryptedString; + this.number = o.number?.encryptedString; + this.expMonth = o.expMonth?.encryptedString; + this.expYear = o.expYear?.encryptedString; + this.code = o.code?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/cipher.ts b/jslib/common/src/models/export/cipher.ts new file mode 100644 index 00000000..5d650666 --- /dev/null +++ b/jslib/common/src/models/export/cipher.ts @@ -0,0 +1,156 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { Cipher as CipherDomain } from "../domain/cipher"; +import { EncString } from "../domain/encString"; +import { CipherView } from "../view/cipherView"; + +import { Card } from "./card"; +import { Field } from "./field"; +import { Identity } from "./identity"; +import { Login } from "./login"; +import { SecureNote } from "./secureNote"; + +export class Cipher { + static template(): Cipher { + const req = new Cipher(); + req.organizationId = null; + req.collectionIds = null; + req.folderId = null; + req.type = CipherType.Login; + req.name = "Item name"; + req.notes = "Some notes about this item."; + req.favorite = false; + req.fields = []; + req.login = null; + req.secureNote = null; + req.card = null; + req.identity = null; + req.reprompt = CipherRepromptType.None; + return req; + } + + static toView(req: Cipher, view = new CipherView()) { + view.type = req.type; + view.folderId = req.folderId; + if (view.organizationId == null) { + view.organizationId = req.organizationId; + } + if (view.collectionIds || req.collectionIds) { + const set = new Set((view.collectionIds ?? []).concat(req.collectionIds ?? [])); + view.collectionIds = Array.from(set.values()); + } + view.name = req.name; + view.notes = req.notes; + view.favorite = req.favorite; + view.reprompt = req.reprompt ?? CipherRepromptType.None; + + if (req.fields != null) { + view.fields = req.fields.map((f) => Field.toView(f)); + } + + switch (req.type) { + case CipherType.Login: + view.login = Login.toView(req.login); + break; + case CipherType.SecureNote: + view.secureNote = SecureNote.toView(req.secureNote); + break; + case CipherType.Card: + view.card = Card.toView(req.card); + break; + case CipherType.Identity: + view.identity = Identity.toView(req.identity); + break; + } + + return view; + } + + static toDomain(req: Cipher, domain = new CipherDomain()) { + domain.type = req.type; + domain.folderId = req.folderId; + if (domain.organizationId == null) { + domain.organizationId = req.organizationId; + } + domain.name = req.name != null ? new EncString(req.name) : null; + domain.notes = req.notes != null ? new EncString(req.notes) : null; + domain.favorite = req.favorite; + domain.reprompt = req.reprompt ?? CipherRepromptType.None; + + if (req.fields != null) { + domain.fields = req.fields.map((f) => Field.toDomain(f)); + } + + switch (req.type) { + case CipherType.Login: + domain.login = Login.toDomain(req.login); + break; + case CipherType.SecureNote: + domain.secureNote = SecureNote.toDomain(req.secureNote); + break; + case CipherType.Card: + domain.card = Card.toDomain(req.card); + break; + case CipherType.Identity: + domain.identity = Identity.toDomain(req.identity); + break; + } + + return domain; + } + + type: CipherType; + folderId: string; + organizationId: string; + collectionIds: string[]; + name: string; + notes: string; + favorite: boolean; + fields: Field[]; + login: Login; + secureNote: SecureNote; + card: Card; + identity: Identity; + reprompt: CipherRepromptType; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CipherView | CipherDomain) { + this.organizationId = o.organizationId; + this.folderId = o.folderId; + this.type = o.type; + this.reprompt = o.reprompt; + + if (o instanceof CipherView) { + this.name = o.name; + this.notes = o.notes; + } else { + this.name = o.name?.encryptedString; + this.notes = o.notes?.encryptedString; + } + + this.favorite = o.favorite; + + if (o.fields != null) { + if (o instanceof CipherView) { + this.fields = o.fields.map((f) => new Field(f)); + } else { + this.fields = o.fields.map((f) => new Field(f)); + } + } + + switch (o.type) { + case CipherType.Login: + this.login = new Login(o.login); + break; + case CipherType.SecureNote: + this.secureNote = new SecureNote(o.secureNote); + break; + case CipherType.Card: + this.card = new Card(o.card); + break; + case CipherType.Identity: + this.identity = new Identity(o.identity); + break; + } + } +} diff --git a/jslib/common/src/models/export/cipherWithIds.ts b/jslib/common/src/models/export/cipherWithIds.ts new file mode 100644 index 00000000..a5698c6d --- /dev/null +++ b/jslib/common/src/models/export/cipherWithIds.ts @@ -0,0 +1,16 @@ +import { Cipher as CipherDomain } from "../domain/cipher"; +import { CipherView } from "../view/cipherView"; + +import { Cipher } from "./cipher"; + +export class CipherWithIds extends Cipher { + id: string; + collectionIds: string[]; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CipherView | CipherDomain) { + this.id = o.id; + super.build(o); + this.collectionIds = o.collectionIds; + } +} diff --git a/jslib/common/src/models/export/collection.ts b/jslib/common/src/models/export/collection.ts new file mode 100644 index 00000000..fba9af15 --- /dev/null +++ b/jslib/common/src/models/export/collection.ts @@ -0,0 +1,46 @@ +import { Collection as CollectionDomain } from "../domain/collection"; +import { EncString } from "../domain/encString"; +import { CollectionView } from "../view/collectionView"; + +export class Collection { + static template(): Collection { + const req = new Collection(); + req.organizationId = "00000000-0000-0000-0000-000000000000"; + req.name = "Collection name"; + req.externalId = null; + return req; + } + + static toView(req: Collection, view = new CollectionView()) { + view.name = req.name; + view.externalId = req.externalId; + if (view.organizationId == null) { + view.organizationId = req.organizationId; + } + return view; + } + + static toDomain(req: Collection, domain = new CollectionDomain()) { + domain.name = req.name != null ? new EncString(req.name) : null; + domain.externalId = req.externalId; + if (domain.organizationId == null) { + domain.organizationId = req.organizationId; + } + return domain; + } + + organizationId: string; + name: string; + externalId: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CollectionView | CollectionDomain) { + this.organizationId = o.organizationId; + if (o instanceof CollectionView) { + this.name = o.name; + } else { + this.name = o.name?.encryptedString; + } + this.externalId = o.externalId; + } +} diff --git a/jslib/common/src/models/export/collectionWithId.ts b/jslib/common/src/models/export/collectionWithId.ts new file mode 100644 index 00000000..7bc0707a --- /dev/null +++ b/jslib/common/src/models/export/collectionWithId.ts @@ -0,0 +1,14 @@ +import { Collection as CollectionDomain } from "../domain/collection"; +import { CollectionView } from "../view/collectionView"; + +import { Collection } from "./collection"; + +export class CollectionWithId extends Collection { + id: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: CollectionView | CollectionDomain) { + this.id = o.id; + super.build(o); + } +} diff --git a/jslib/common/src/models/export/event.ts b/jslib/common/src/models/export/event.ts new file mode 100644 index 00000000..09975f2f --- /dev/null +++ b/jslib/common/src/models/export/event.ts @@ -0,0 +1,26 @@ +import { EventType } from "../../enums/eventType"; +import { EventView } from "../view/eventView"; + +export class Event { + message: string; + appIcon: string; + appName: string; + userId: string; + userName: string; + userEmail: string; + date: string; + ip: string; + type: string; + + constructor(event: EventView) { + this.message = event.humanReadableMessage; + this.appIcon = event.appIcon; + this.appName = event.appName; + this.userId = event.userId; + this.userName = event.userName; + this.userEmail = event.userEmail; + this.date = event.date; + this.ip = event.ip; + this.type = EventType[event.type]; + } +} diff --git a/jslib/common/src/models/export/field.ts b/jslib/common/src/models/export/field.ts new file mode 100644 index 00000000..c8ce7581 --- /dev/null +++ b/jslib/common/src/models/export/field.ts @@ -0,0 +1,52 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { EncString } from "../domain/encString"; +import { Field as FieldDomain } from "../domain/field"; +import { FieldView } from "../view/fieldView"; + +export class Field { + static template(): Field { + const req = new Field(); + req.name = "Field name"; + req.value = "Some value"; + req.type = FieldType.Text; + return req; + } + + static toView(req: Field, view = new FieldView()) { + view.type = req.type; + view.value = req.value; + view.name = req.name; + view.linkedId = req.linkedId; + return view; + } + + static toDomain(req: Field, domain = new FieldDomain()) { + domain.type = req.type; + domain.value = req.value != null ? new EncString(req.value) : null; + domain.name = req.name != null ? new EncString(req.name) : null; + domain.linkedId = req.linkedId; + return domain; + } + + name: string; + value: string; + type: FieldType; + linkedId: LinkedIdType; + + constructor(o?: FieldView | FieldDomain) { + if (o == null) { + return; + } + + if (o instanceof FieldView) { + this.name = o.name; + this.value = o.value; + } else { + this.name = o.name?.encryptedString; + this.value = o.value?.encryptedString; + } + this.type = o.type; + this.linkedId = o.linkedId; + } +} diff --git a/jslib/common/src/models/export/folder.ts b/jslib/common/src/models/export/folder.ts new file mode 100644 index 00000000..e770f326 --- /dev/null +++ b/jslib/common/src/models/export/folder.ts @@ -0,0 +1,32 @@ +import { EncString } from "../domain/encString"; +import { Folder as FolderDomain } from "../domain/folder"; +import { FolderView } from "../view/folderView"; + +export class Folder { + static template(): Folder { + const req = new Folder(); + req.name = "Folder name"; + return req; + } + + static toView(req: Folder, view = new FolderView()) { + view.name = req.name; + return view; + } + + static toDomain(req: Folder, domain = new FolderDomain()) { + domain.name = req.name != null ? new EncString(req.name) : null; + return domain; + } + + name: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: FolderView | FolderDomain) { + if (o instanceof FolderView) { + this.name = o.name; + } else { + this.name = o.name?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/folderWithId.ts b/jslib/common/src/models/export/folderWithId.ts new file mode 100644 index 00000000..a5507d51 --- /dev/null +++ b/jslib/common/src/models/export/folderWithId.ts @@ -0,0 +1,14 @@ +import { Folder as FolderDomain } from "../domain/folder"; +import { FolderView } from "../view/folderView"; + +import { Folder } from "./folder"; + +export class FolderWithId extends Folder { + id: string; + + // Use build method instead of ctor so that we can control order of JSON stringify for pretty print + build(o: FolderView | FolderDomain) { + this.id = o.id; + super.build(o); + } +} diff --git a/jslib/common/src/models/export/identity.ts b/jslib/common/src/models/export/identity.ts new file mode 100644 index 00000000..144732db --- /dev/null +++ b/jslib/common/src/models/export/identity.ts @@ -0,0 +1,137 @@ +import { EncString } from "../domain/encString"; +import { Identity as IdentityDomain } from "../domain/identity"; +import { IdentityView } from "../view/identityView"; + +export class Identity { + static template(): Identity { + const req = new Identity(); + req.title = "Mr"; + req.firstName = "John"; + req.middleName = "William"; + req.lastName = "Doe"; + req.address1 = "123 Any St"; + req.address2 = "Apt #123"; + req.address3 = null; + req.city = "New York"; + req.state = "NY"; + req.postalCode = "10001"; + req.country = "US"; + req.company = "Acme Inc."; + req.email = "john@company.com"; + req.phone = "5555551234"; + req.ssn = "000-123-4567"; + req.username = "jdoe"; + req.passportNumber = "US-123456789"; + req.licenseNumber = "D123-12-123-12333"; + return req; + } + + static toView(req: Identity, view = new IdentityView()) { + view.title = req.title; + view.firstName = req.firstName; + view.middleName = req.middleName; + view.lastName = req.lastName; + view.address1 = req.address1; + view.address2 = req.address2; + view.address3 = req.address3; + view.city = req.city; + view.state = req.state; + view.postalCode = req.postalCode; + view.country = req.country; + view.company = req.company; + view.email = req.email; + view.phone = req.phone; + view.ssn = req.ssn; + view.username = req.username; + view.passportNumber = req.passportNumber; + view.licenseNumber = req.licenseNumber; + return view; + } + + static toDomain(req: Identity, domain = new IdentityDomain()) { + domain.title = req.title != null ? new EncString(req.title) : null; + domain.firstName = req.firstName != null ? new EncString(req.firstName) : null; + domain.middleName = req.middleName != null ? new EncString(req.middleName) : null; + domain.lastName = req.lastName != null ? new EncString(req.lastName) : null; + domain.address1 = req.address1 != null ? new EncString(req.address1) : null; + domain.address2 = req.address2 != null ? new EncString(req.address2) : null; + domain.address3 = req.address3 != null ? new EncString(req.address3) : null; + domain.city = req.city != null ? new EncString(req.city) : null; + domain.state = req.state != null ? new EncString(req.state) : null; + domain.postalCode = req.postalCode != null ? new EncString(req.postalCode) : null; + domain.country = req.country != null ? new EncString(req.country) : null; + domain.company = req.company != null ? new EncString(req.company) : null; + domain.email = req.email != null ? new EncString(req.email) : null; + domain.phone = req.phone != null ? new EncString(req.phone) : null; + domain.ssn = req.ssn != null ? new EncString(req.ssn) : null; + domain.username = req.username != null ? new EncString(req.username) : null; + domain.passportNumber = req.passportNumber != null ? new EncString(req.passportNumber) : null; + domain.licenseNumber = req.licenseNumber != null ? new EncString(req.licenseNumber) : null; + return domain; + } + + title: string; + firstName: string; + middleName: string; + lastName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + email: string; + phone: string; + ssn: string; + username: string; + passportNumber: string; + licenseNumber: string; + + constructor(o?: IdentityView | IdentityDomain) { + if (o == null) { + return; + } + + if (o instanceof IdentityView) { + this.title = o.title; + this.firstName = o.firstName; + this.middleName = o.middleName; + this.lastName = o.lastName; + this.address1 = o.address1; + this.address2 = o.address2; + this.address3 = o.address3; + this.city = o.city; + this.state = o.state; + this.postalCode = o.postalCode; + this.country = o.country; + this.company = o.company; + this.email = o.email; + this.phone = o.phone; + this.ssn = o.ssn; + this.username = o.username; + this.passportNumber = o.passportNumber; + this.licenseNumber = o.licenseNumber; + } else { + this.title = o.title?.encryptedString; + this.firstName = o.firstName?.encryptedString; + this.middleName = o.middleName?.encryptedString; + this.lastName = o.lastName?.encryptedString; + this.address1 = o.address1?.encryptedString; + this.address2 = o.address2?.encryptedString; + this.address3 = o.address3?.encryptedString; + this.city = o.city?.encryptedString; + this.state = o.state?.encryptedString; + this.postalCode = o.postalCode?.encryptedString; + this.country = o.country?.encryptedString; + this.company = o.company?.encryptedString; + this.email = o.email?.encryptedString; + this.phone = o.phone?.encryptedString; + this.ssn = o.ssn?.encryptedString; + this.username = o.username?.encryptedString; + this.passportNumber = o.passportNumber?.encryptedString; + this.licenseNumber = o.licenseNumber?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/login.ts b/jslib/common/src/models/export/login.ts new file mode 100644 index 00000000..6a07ff74 --- /dev/null +++ b/jslib/common/src/models/export/login.ts @@ -0,0 +1,65 @@ +import { EncString } from "../domain/encString"; +import { Login as LoginDomain } from "../domain/login"; +import { LoginView } from "../view/loginView"; + +import { LoginUri } from "./loginUri"; + +export class Login { + static template(): Login { + const req = new Login(); + req.uris = []; + req.username = "jdoe"; + req.password = "myp@ssword123"; + req.totp = "JBSWY3DPEHPK3PXP"; + return req; + } + + static toView(req: Login, view = new LoginView()) { + if (req.uris != null) { + view.uris = req.uris.map((u) => LoginUri.toView(u)); + } + view.username = req.username; + view.password = req.password; + view.totp = req.totp; + return view; + } + + static toDomain(req: Login, domain = new LoginDomain()) { + if (req.uris != null) { + domain.uris = req.uris.map((u) => LoginUri.toDomain(u)); + } + domain.username = req.username != null ? new EncString(req.username) : null; + domain.password = req.password != null ? new EncString(req.password) : null; + domain.totp = req.totp != null ? new EncString(req.totp) : null; + return domain; + } + + uris: LoginUri[]; + username: string; + password: string; + totp: string; + + constructor(o?: LoginView | LoginDomain) { + if (o == null) { + return; + } + + if (o.uris != null) { + if (o instanceof LoginView) { + this.uris = o.uris.map((u) => new LoginUri(u)); + } else { + this.uris = o.uris.map((u) => new LoginUri(u)); + } + } + + if (o instanceof LoginView) { + this.username = o.username; + this.password = o.password; + this.totp = o.totp; + } else { + this.username = o.username?.encryptedString; + this.password = o.password?.encryptedString; + this.totp = o.totp?.encryptedString; + } + } +} diff --git a/jslib/common/src/models/export/loginUri.ts b/jslib/common/src/models/export/loginUri.ts new file mode 100644 index 00000000..3e6d588b --- /dev/null +++ b/jslib/common/src/models/export/loginUri.ts @@ -0,0 +1,41 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { EncString } from "../domain/encString"; +import { LoginUri as LoginUriDomain } from "../domain/loginUri"; +import { LoginUriView } from "../view/loginUriView"; + +export class LoginUri { + static template(): LoginUri { + const req = new LoginUri(); + req.uri = "https://google.com"; + req.match = null; + return req; + } + + static toView(req: LoginUri, view = new LoginUriView()) { + view.uri = req.uri; + view.match = req.match; + return view; + } + + static toDomain(req: LoginUri, domain = new LoginUriDomain()) { + domain.uri = req.uri != null ? new EncString(req.uri) : null; + domain.match = req.match; + return domain; + } + + uri: string; + match: UriMatchType = null; + + constructor(o?: LoginUriView | LoginUriDomain) { + if (o == null) { + return; + } + + if (o instanceof LoginUriView) { + this.uri = o.uri; + } else { + this.uri = o.uri?.encryptedString; + } + this.match = o.match; + } +} diff --git a/jslib/common/src/models/export/secureNote.ts b/jslib/common/src/models/export/secureNote.ts new file mode 100644 index 00000000..851ba8e4 --- /dev/null +++ b/jslib/common/src/models/export/secureNote.ts @@ -0,0 +1,31 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNote as SecureNoteDomain } from "../domain/secureNote"; +import { SecureNoteView } from "../view/secureNoteView"; + +export class SecureNote { + static template(): SecureNote { + const req = new SecureNote(); + req.type = SecureNoteType.Generic; + return req; + } + + static toView(req: SecureNote, view = new SecureNoteView()) { + view.type = req.type; + return view; + } + + static toDomain(req: SecureNote, view = new SecureNoteDomain()) { + view.type = req.type; + return view; + } + + type: SecureNoteType; + + constructor(o?: SecureNoteView | SecureNoteDomain) { + if (o == null) { + return; + } + + this.type = o.type; + } +} diff --git a/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts b/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts new file mode 100644 index 00000000..a84cc7ef --- /dev/null +++ b/jslib/common/src/models/request/account/setKeyConnectorKeyRequest.ts @@ -0,0 +1,24 @@ +import { KdfType } from "../../../enums/kdfType"; +import { KeysRequest } from "../keysRequest"; + +export class SetKeyConnectorKeyRequest { + key: string; + keys: KeysRequest; + kdf: KdfType; + kdfIterations: number; + orgIdentifier: string; + + constructor( + key: string, + kdf: KdfType, + kdfIterations: number, + orgIdentifier: string, + keys: KeysRequest + ) { + this.key = key; + this.kdf = kdf; + this.kdfIterations = kdfIterations; + this.orgIdentifier = orgIdentifier; + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/account/verifyOTPRequest.ts b/jslib/common/src/models/request/account/verifyOTPRequest.ts new file mode 100644 index 00000000..2eb8816e --- /dev/null +++ b/jslib/common/src/models/request/account/verifyOTPRequest.ts @@ -0,0 +1,7 @@ +export class VerifyOTPRequest { + OTP: string; + + constructor(OTP: string) { + this.OTP = OTP; + } +} diff --git a/jslib/common/src/models/request/attachmentRequest.ts b/jslib/common/src/models/request/attachmentRequest.ts new file mode 100644 index 00000000..ea1ea821 --- /dev/null +++ b/jslib/common/src/models/request/attachmentRequest.ts @@ -0,0 +1,6 @@ +export class AttachmentRequest { + fileName: string; + key: string; + fileSize: number; + adminRequest: boolean; +} diff --git a/jslib/common/src/models/request/bitPayInvoiceRequest.ts b/jslib/common/src/models/request/bitPayInvoiceRequest.ts new file mode 100644 index 00000000..9042611b --- /dev/null +++ b/jslib/common/src/models/request/bitPayInvoiceRequest.ts @@ -0,0 +1,9 @@ +export class BitPayInvoiceRequest { + userId: string; + organizationId: string; + credit: boolean; + amount: number; + returnUrl: string; + name: string; + email: string; +} diff --git a/jslib/common/src/models/request/captchaProtectedRequest.ts b/jslib/common/src/models/request/captchaProtectedRequest.ts new file mode 100644 index 00000000..54721e01 --- /dev/null +++ b/jslib/common/src/models/request/captchaProtectedRequest.ts @@ -0,0 +1,3 @@ +export abstract class CaptchaProtectedRequest { + captchaResponse: string = null; +} diff --git a/jslib/common/src/models/request/cipherBulkDeleteRequest.ts b/jslib/common/src/models/request/cipherBulkDeleteRequest.ts new file mode 100644 index 00000000..227f1a66 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkDeleteRequest.ts @@ -0,0 +1,9 @@ +export class CipherBulkDeleteRequest { + ids: string[]; + organizationId: string; + + constructor(ids: string[], organizationId?: string) { + this.ids = ids == null ? [] : ids; + this.organizationId = organizationId; + } +} diff --git a/jslib/common/src/models/request/cipherBulkMoveRequest.ts b/jslib/common/src/models/request/cipherBulkMoveRequest.ts new file mode 100644 index 00000000..06a737de --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkMoveRequest.ts @@ -0,0 +1,9 @@ +export class CipherBulkMoveRequest { + ids: string[]; + folderId: string; + + constructor(ids: string[], folderId: string) { + this.ids = ids == null ? [] : ids; + this.folderId = folderId; + } +} diff --git a/jslib/common/src/models/request/cipherBulkRestoreRequest.ts b/jslib/common/src/models/request/cipherBulkRestoreRequest.ts new file mode 100644 index 00000000..70e5a4e8 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkRestoreRequest.ts @@ -0,0 +1,7 @@ +export class CipherBulkRestoreRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/cipherBulkShareRequest.ts b/jslib/common/src/models/request/cipherBulkShareRequest.ts new file mode 100644 index 00000000..6e77ad56 --- /dev/null +++ b/jslib/common/src/models/request/cipherBulkShareRequest.ts @@ -0,0 +1,18 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherWithIdRequest } from "./cipherWithIdRequest"; + +export class CipherBulkShareRequest { + ciphers: CipherWithIdRequest[]; + collectionIds: string[]; + + constructor(ciphers: Cipher[], collectionIds: string[]) { + if (ciphers != null) { + this.ciphers = []; + ciphers.forEach((c) => { + this.ciphers.push(new CipherWithIdRequest(c)); + }); + } + this.collectionIds = collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherCollectionsRequest.ts b/jslib/common/src/models/request/cipherCollectionsRequest.ts new file mode 100644 index 00000000..8d555389 --- /dev/null +++ b/jslib/common/src/models/request/cipherCollectionsRequest.ts @@ -0,0 +1,7 @@ +export class CipherCollectionsRequest { + collectionIds: string[]; + + constructor(collectionIds: string[]) { + this.collectionIds = collectionIds == null ? [] : collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherCreateRequest.ts b/jslib/common/src/models/request/cipherCreateRequest.ts new file mode 100644 index 00000000..d7ff5128 --- /dev/null +++ b/jslib/common/src/models/request/cipherCreateRequest.ts @@ -0,0 +1,13 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherCreateRequest { + cipher: CipherRequest; + collectionIds: string[]; + + constructor(cipher: Cipher) { + this.cipher = new CipherRequest(cipher); + this.collectionIds = cipher.collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherRequest.ts b/jslib/common/src/models/request/cipherRequest.ts new file mode 100644 index 00000000..4ed58850 --- /dev/null +++ b/jslib/common/src/models/request/cipherRequest.ts @@ -0,0 +1,164 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { CardApi } from "../api/cardApi"; +import { FieldApi } from "../api/fieldApi"; +import { IdentityApi } from "../api/identityApi"; +import { LoginApi } from "../api/loginApi"; +import { LoginUriApi } from "../api/loginUriApi"; +import { SecureNoteApi } from "../api/secureNoteApi"; +import { Cipher } from "../domain/cipher"; + +import { AttachmentRequest } from "./attachmentRequest"; +import { PasswordHistoryRequest } from "./passwordHistoryRequest"; + +export class CipherRequest { + type: CipherType; + folderId: string; + organizationId: string; + name: string; + notes: string; + favorite: boolean; + login: LoginApi; + secureNote: SecureNoteApi; + card: CardApi; + identity: IdentityApi; + fields: FieldApi[]; + passwordHistory: PasswordHistoryRequest[]; + // Deprecated, remove at some point and rename attachments2 to attachments + attachments: { [id: string]: string }; + attachments2: { [id: string]: AttachmentRequest }; + lastKnownRevisionDate: Date; + reprompt: CipherRepromptType; + + constructor(cipher: Cipher) { + this.type = cipher.type; + this.folderId = cipher.folderId; + this.organizationId = cipher.organizationId; + this.name = cipher.name ? cipher.name.encryptedString : null; + this.notes = cipher.notes ? cipher.notes.encryptedString : null; + this.favorite = cipher.favorite; + this.lastKnownRevisionDate = cipher.revisionDate; + this.reprompt = cipher.reprompt; + + switch (this.type) { + case CipherType.Login: + this.login = new LoginApi(); + this.login.uris = null; + this.login.username = cipher.login.username ? cipher.login.username.encryptedString : null; + this.login.password = cipher.login.password ? cipher.login.password.encryptedString : null; + this.login.passwordRevisionDate = + cipher.login.passwordRevisionDate != null + ? cipher.login.passwordRevisionDate.toISOString() + : null; + this.login.totp = cipher.login.totp ? cipher.login.totp.encryptedString : null; + this.login.autofillOnPageLoad = cipher.login.autofillOnPageLoad; + + if (cipher.login.uris != null) { + this.login.uris = cipher.login.uris.map((u) => { + const uri = new LoginUriApi(); + uri.uri = u.uri != null ? u.uri.encryptedString : null; + uri.match = u.match != null ? u.match : null; + return uri; + }); + } + break; + case CipherType.SecureNote: + this.secureNote = new SecureNoteApi(); + this.secureNote.type = cipher.secureNote.type; + break; + case CipherType.Card: + this.card = new CardApi(); + this.card.cardholderName = + cipher.card.cardholderName != null ? cipher.card.cardholderName.encryptedString : null; + this.card.brand = cipher.card.brand != null ? cipher.card.brand.encryptedString : null; + this.card.number = cipher.card.number != null ? cipher.card.number.encryptedString : null; + this.card.expMonth = + cipher.card.expMonth != null ? cipher.card.expMonth.encryptedString : null; + this.card.expYear = + cipher.card.expYear != null ? cipher.card.expYear.encryptedString : null; + this.card.code = cipher.card.code != null ? cipher.card.code.encryptedString : null; + break; + case CipherType.Identity: + this.identity = new IdentityApi(); + this.identity.title = + cipher.identity.title != null ? cipher.identity.title.encryptedString : null; + this.identity.firstName = + cipher.identity.firstName != null ? cipher.identity.firstName.encryptedString : null; + this.identity.middleName = + cipher.identity.middleName != null ? cipher.identity.middleName.encryptedString : null; + this.identity.lastName = + cipher.identity.lastName != null ? cipher.identity.lastName.encryptedString : null; + this.identity.address1 = + cipher.identity.address1 != null ? cipher.identity.address1.encryptedString : null; + this.identity.address2 = + cipher.identity.address2 != null ? cipher.identity.address2.encryptedString : null; + this.identity.address3 = + cipher.identity.address3 != null ? cipher.identity.address3.encryptedString : null; + this.identity.city = + cipher.identity.city != null ? cipher.identity.city.encryptedString : null; + this.identity.state = + cipher.identity.state != null ? cipher.identity.state.encryptedString : null; + this.identity.postalCode = + cipher.identity.postalCode != null ? cipher.identity.postalCode.encryptedString : null; + this.identity.country = + cipher.identity.country != null ? cipher.identity.country.encryptedString : null; + this.identity.company = + cipher.identity.company != null ? cipher.identity.company.encryptedString : null; + this.identity.email = + cipher.identity.email != null ? cipher.identity.email.encryptedString : null; + this.identity.phone = + cipher.identity.phone != null ? cipher.identity.phone.encryptedString : null; + this.identity.ssn = + cipher.identity.ssn != null ? cipher.identity.ssn.encryptedString : null; + this.identity.username = + cipher.identity.username != null ? cipher.identity.username.encryptedString : null; + this.identity.passportNumber = + cipher.identity.passportNumber != null + ? cipher.identity.passportNumber.encryptedString + : null; + this.identity.licenseNumber = + cipher.identity.licenseNumber != null + ? cipher.identity.licenseNumber.encryptedString + : null; + break; + default: + break; + } + + if (cipher.fields != null) { + this.fields = cipher.fields.map((f) => { + const field = new FieldApi(); + field.type = f.type; + field.name = f.name ? f.name.encryptedString : null; + field.value = f.value ? f.value.encryptedString : null; + field.linkedId = f.linkedId; + return field; + }); + } + + if (cipher.passwordHistory != null) { + this.passwordHistory = []; + cipher.passwordHistory.forEach((ph) => { + this.passwordHistory.push({ + lastUsedDate: ph.lastUsedDate, + password: ph.password ? ph.password.encryptedString : null, + }); + }); + } + + if (cipher.attachments != null) { + this.attachments = {}; + this.attachments2 = {}; + cipher.attachments.forEach((attachment) => { + const fileName = attachment.fileName ? attachment.fileName.encryptedString : null; + this.attachments[attachment.id] = fileName; + const attachmentRequest = new AttachmentRequest(); + attachmentRequest.fileName = fileName; + if (attachment.key != null) { + attachmentRequest.key = attachment.key.encryptedString; + } + this.attachments2[attachment.id] = attachmentRequest; + }); + } + } +} diff --git a/jslib/common/src/models/request/cipherShareRequest.ts b/jslib/common/src/models/request/cipherShareRequest.ts new file mode 100644 index 00000000..4cf6dfde --- /dev/null +++ b/jslib/common/src/models/request/cipherShareRequest.ts @@ -0,0 +1,13 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherShareRequest { + cipher: CipherRequest; + collectionIds: string[]; + + constructor(cipher: Cipher) { + this.cipher = new CipherRequest(cipher); + this.collectionIds = cipher.collectionIds; + } +} diff --git a/jslib/common/src/models/request/cipherWithIdRequest.ts b/jslib/common/src/models/request/cipherWithIdRequest.ts new file mode 100644 index 00000000..0f22322f --- /dev/null +++ b/jslib/common/src/models/request/cipherWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Cipher } from "../domain/cipher"; + +import { CipherRequest } from "./cipherRequest"; + +export class CipherWithIdRequest extends CipherRequest { + id: string; + + constructor(cipher: Cipher) { + super(cipher); + this.id = cipher.id; + } +} diff --git a/jslib/common/src/models/request/collectionRequest.ts b/jslib/common/src/models/request/collectionRequest.ts new file mode 100644 index 00000000..c2176095 --- /dev/null +++ b/jslib/common/src/models/request/collectionRequest.ts @@ -0,0 +1,17 @@ +import { Collection } from "../domain/collection"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class CollectionRequest { + name: string; + externalId: string; + groups: SelectionReadOnlyRequest[] = []; + + constructor(collection?: Collection) { + if (collection == null) { + return; + } + this.name = collection.name ? collection.name.encryptedString : null; + this.externalId = collection.externalId; + } +} diff --git a/jslib/common/src/models/request/deleteRecoverRequest.ts b/jslib/common/src/models/request/deleteRecoverRequest.ts new file mode 100644 index 00000000..02a019b8 --- /dev/null +++ b/jslib/common/src/models/request/deleteRecoverRequest.ts @@ -0,0 +1,3 @@ +export class DeleteRecoverRequest { + email: string; +} diff --git a/jslib/common/src/models/request/deviceRequest.ts b/jslib/common/src/models/request/deviceRequest.ts new file mode 100644 index 00000000..66c17b57 --- /dev/null +++ b/jslib/common/src/models/request/deviceRequest.ts @@ -0,0 +1,16 @@ +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { DeviceType } from "../../enums/deviceType"; + +export class DeviceRequest { + type: DeviceType; + name: string; + identifier: string; + pushToken?: string; + + constructor(appId: string, platformUtilsService: PlatformUtilsService) { + this.type = platformUtilsService.getDevice(); + this.name = platformUtilsService.getDeviceString(); + this.identifier = appId; + this.pushToken = null; + } +} diff --git a/jslib/common/src/models/request/deviceTokenRequest.ts b/jslib/common/src/models/request/deviceTokenRequest.ts new file mode 100644 index 00000000..99ca69a2 --- /dev/null +++ b/jslib/common/src/models/request/deviceTokenRequest.ts @@ -0,0 +1,7 @@ +export class DeviceTokenRequest { + pushToken: string; + + constructor() { + this.pushToken = null; + } +} diff --git a/jslib/common/src/models/request/emailRequest.ts b/jslib/common/src/models/request/emailRequest.ts new file mode 100644 index 00000000..0b559615 --- /dev/null +++ b/jslib/common/src/models/request/emailRequest.ts @@ -0,0 +1,7 @@ +import { EmailTokenRequest } from "./emailTokenRequest"; + +export class EmailRequest extends EmailTokenRequest { + newMasterPasswordHash: string; + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/emailTokenRequest.ts b/jslib/common/src/models/request/emailTokenRequest.ts new file mode 100644 index 00000000..7e0515ca --- /dev/null +++ b/jslib/common/src/models/request/emailTokenRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class EmailTokenRequest extends SecretVerificationRequest { + newEmail: string; + masterPasswordHash: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts b/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts new file mode 100644 index 00000000..1cb10253 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessAcceptRequest.ts @@ -0,0 +1,3 @@ +export class EmergencyAccessAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts b/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts new file mode 100644 index 00000000..ee54a4fe --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessConfirmRequest.ts @@ -0,0 +1,3 @@ +export class EmergencyAccessConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessInviteRequest.ts b/jslib/common/src/models/request/emergencyAccessInviteRequest.ts new file mode 100644 index 00000000..d75ed419 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessInviteRequest.ts @@ -0,0 +1,7 @@ +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; + +export class EmergencyAccessInviteRequest { + email: string; + type: EmergencyAccessType; + waitTimeDays: number; +} diff --git a/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts b/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts new file mode 100644 index 00000000..3fb459e1 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessPasswordRequest.ts @@ -0,0 +1,4 @@ +export class EmergencyAccessPasswordRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts b/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts new file mode 100644 index 00000000..d7c55c94 --- /dev/null +++ b/jslib/common/src/models/request/emergencyAccessUpdateRequest.ts @@ -0,0 +1,7 @@ +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; + +export class EmergencyAccessUpdateRequest { + type: EmergencyAccessType; + waitTimeDays: number; + keyEncrypted?: string; +} diff --git a/jslib/common/src/models/request/eventRequest.ts b/jslib/common/src/models/request/eventRequest.ts new file mode 100644 index 00000000..a6228fd6 --- /dev/null +++ b/jslib/common/src/models/request/eventRequest.ts @@ -0,0 +1,7 @@ +import { EventType } from "../../enums/eventType"; + +export class EventRequest { + type: EventType; + cipherId: string; + date: string; +} diff --git a/jslib/common/src/models/request/folderRequest.ts b/jslib/common/src/models/request/folderRequest.ts new file mode 100644 index 00000000..a37f66dd --- /dev/null +++ b/jslib/common/src/models/request/folderRequest.ts @@ -0,0 +1,9 @@ +import { Folder } from "../domain/folder"; + +export class FolderRequest { + name: string; + + constructor(folder: Folder) { + this.name = folder.name ? folder.name.encryptedString : null; + } +} diff --git a/jslib/common/src/models/request/folderWithIdRequest.ts b/jslib/common/src/models/request/folderWithIdRequest.ts new file mode 100644 index 00000000..a36c2b70 --- /dev/null +++ b/jslib/common/src/models/request/folderWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Folder } from "../domain/folder"; + +import { FolderRequest } from "./folderRequest"; + +export class FolderWithIdRequest extends FolderRequest { + id: string; + + constructor(folder: Folder) { + super(folder); + this.id = folder.id; + } +} diff --git a/jslib/common/src/models/request/groupRequest.ts b/jslib/common/src/models/request/groupRequest.ts new file mode 100644 index 00000000..c4c349c8 --- /dev/null +++ b/jslib/common/src/models/request/groupRequest.ts @@ -0,0 +1,8 @@ +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class GroupRequest { + name: string; + accessAll: boolean; + externalId: string; + collections: SelectionReadOnlyRequest[] = []; +} diff --git a/jslib/common/src/models/request/iapCheckRequest.ts b/jslib/common/src/models/request/iapCheckRequest.ts new file mode 100644 index 00000000..b8796e8c --- /dev/null +++ b/jslib/common/src/models/request/iapCheckRequest.ts @@ -0,0 +1,5 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; + +export class IapCheckRequest { + paymentMethodType: PaymentMethodType; +} diff --git a/jslib/common/src/models/request/identityToken/apiTokenRequest.ts b/jslib/common/src/models/request/identityToken/apiTokenRequest.ts new file mode 100644 index 00000000..247ba174 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/apiTokenRequest.ts @@ -0,0 +1,25 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class ApiTokenRequest extends TokenRequest { + constructor( + public clientId: string, + public clientSecret: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken() { + const obj = super.toIdentityToken(this.clientId); + + obj.scope = this.clientId.startsWith("organization") ? "api.organization" : "api"; + obj.grant_type = "client_credentials"; + obj.client_secret = this.clientSecret; + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts b/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts new file mode 100644 index 00000000..eda10037 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/passwordTokenRequest.ts @@ -0,0 +1,37 @@ +import { ClientType } from "../../../enums/clientType"; +import { Utils } from "../../../misc/utils"; +import { CaptchaProtectedRequest } from "../captchaProtectedRequest"; +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class PasswordTokenRequest extends TokenRequest implements CaptchaProtectedRequest { + constructor( + public email: string, + public masterPasswordHash: string, + public captchaResponse: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken(clientId: ClientType) { + const obj = super.toIdentityToken(clientId); + + obj.grant_type = "password"; + obj.username = this.email; + obj.password = this.masterPasswordHash; + + if (this.captchaResponse != null) { + obj.captchaResponse = this.captchaResponse; + } + + return obj; + } + + alterIdentityTokenHeaders(headers: Headers) { + headers.set("Auth-Email", Utils.fromUtf8ToUrlB64(this.email)); + } +} diff --git a/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts b/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts new file mode 100644 index 00000000..c7697c2f --- /dev/null +++ b/jslib/common/src/models/request/identityToken/ssoTokenRequest.ts @@ -0,0 +1,27 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequest } from "./tokenRequest"; +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export class SsoTokenRequest extends TokenRequest { + constructor( + public code: string, + public codeVerifier: string, + public redirectUri: string, + protected twoFactor: TokenRequestTwoFactor, + device?: DeviceRequest + ) { + super(twoFactor, device); + } + + toIdentityToken(clientId: string) { + const obj = super.toIdentityToken(clientId); + + obj.grant_type = "authorization_code"; + obj.code = this.code; + obj.code_verifier = this.codeVerifier; + obj.redirect_uri = this.redirectUri; + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/tokenRequest.ts b/jslib/common/src/models/request/identityToken/tokenRequest.ts new file mode 100644 index 00000000..82a4a394 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/tokenRequest.ts @@ -0,0 +1,43 @@ +import { DeviceRequest } from "../deviceRequest"; + +import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; + +export abstract class TokenRequest { + protected device?: DeviceRequest; + + constructor(protected twoFactor: TokenRequestTwoFactor, device?: DeviceRequest) { + this.device = device != null ? device : null; + } + + // eslint-disable-next-line + alterIdentityTokenHeaders(headers: Headers) { + // Implemented in subclass if required + } + + setTwoFactor(twoFactor: TokenRequestTwoFactor) { + this.twoFactor = twoFactor; + } + + protected toIdentityToken(clientId: string) { + const obj: any = { + scope: "api offline_access", + client_id: clientId, + }; + + if (this.device) { + obj.deviceType = this.device.type; + obj.deviceIdentifier = this.device.identifier; + obj.deviceName = this.device.name; + // no push tokens for browser apps yet + // obj.devicePushToken = this.device.pushToken; + } + + if (this.twoFactor.token && this.twoFactor.provider != null) { + obj.twoFactorToken = this.twoFactor.token; + obj.twoFactorProvider = this.twoFactor.provider; + obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0"; + } + + return obj; + } +} diff --git a/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts b/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts new file mode 100644 index 00000000..567e8c66 --- /dev/null +++ b/jslib/common/src/models/request/identityToken/tokenRequestTwoFactor.ts @@ -0,0 +1,9 @@ +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; + +export class TokenRequestTwoFactor { + constructor( + public provider: TwoFactorProviderType = null, + public token: string = null, + public remember: boolean = false + ) {} +} diff --git a/jslib/common/src/models/request/importCiphersRequest.ts b/jslib/common/src/models/request/importCiphersRequest.ts new file mode 100644 index 00000000..ebaa03a3 --- /dev/null +++ b/jslib/common/src/models/request/importCiphersRequest.ts @@ -0,0 +1,9 @@ +import { CipherRequest } from "./cipherRequest"; +import { FolderRequest } from "./folderRequest"; +import { KvpRequest } from "./kvpRequest"; + +export class ImportCiphersRequest { + ciphers: CipherRequest[] = []; + folders: FolderRequest[] = []; + folderRelationships: KvpRequest[] = []; +} diff --git a/jslib/common/src/models/request/importDirectoryRequest.ts b/jslib/common/src/models/request/importDirectoryRequest.ts new file mode 100644 index 00000000..a13ce456 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequest.ts @@ -0,0 +1,9 @@ +import { ImportDirectoryRequestGroup } from "./importDirectoryRequestGroup"; +import { ImportDirectoryRequestUser } from "./importDirectoryRequestUser"; + +export class ImportDirectoryRequest { + groups: ImportDirectoryRequestGroup[] = []; + users: ImportDirectoryRequestUser[] = []; + overwriteExisting = false; + largeImport = false; +} diff --git a/jslib/common/src/models/request/importDirectoryRequestGroup.ts b/jslib/common/src/models/request/importDirectoryRequestGroup.ts new file mode 100644 index 00000000..4b7b3567 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequestGroup.ts @@ -0,0 +1,5 @@ +export class ImportDirectoryRequestGroup { + name: string; + externalId: string; + users: string[]; +} diff --git a/jslib/common/src/models/request/importDirectoryRequestUser.ts b/jslib/common/src/models/request/importDirectoryRequestUser.ts new file mode 100644 index 00000000..9dbf6a34 --- /dev/null +++ b/jslib/common/src/models/request/importDirectoryRequestUser.ts @@ -0,0 +1,5 @@ +export class ImportDirectoryRequestUser { + externalId: string; + email: string; + deleted: boolean; +} diff --git a/jslib/common/src/models/request/importOrganizationCiphersRequest.ts b/jslib/common/src/models/request/importOrganizationCiphersRequest.ts new file mode 100644 index 00000000..f2936afb --- /dev/null +++ b/jslib/common/src/models/request/importOrganizationCiphersRequest.ts @@ -0,0 +1,9 @@ +import { CipherRequest } from "./cipherRequest"; +import { CollectionRequest } from "./collectionRequest"; +import { KvpRequest } from "./kvpRequest"; + +export class ImportOrganizationCiphersRequest { + ciphers: CipherRequest[] = []; + collections: CollectionRequest[] = []; + collectionRelationships: KvpRequest[] = []; +} diff --git a/jslib/common/src/models/request/kdfRequest.ts b/jslib/common/src/models/request/kdfRequest.ts new file mode 100644 index 00000000..47c1ce07 --- /dev/null +++ b/jslib/common/src/models/request/kdfRequest.ts @@ -0,0 +1,8 @@ +import { KdfType } from "../../enums/kdfType"; + +import { PasswordRequest } from "./passwordRequest"; + +export class KdfRequest extends PasswordRequest { + kdf: KdfType; + kdfIterations: number; +} diff --git a/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts b/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts new file mode 100644 index 00000000..3df2db82 --- /dev/null +++ b/jslib/common/src/models/request/keyConnectorUserKeyRequest.ts @@ -0,0 +1,7 @@ +export class KeyConnectorUserKeyRequest { + key: string; + + constructor(key: string) { + this.key = key; + } +} diff --git a/jslib/common/src/models/request/keysRequest.ts b/jslib/common/src/models/request/keysRequest.ts new file mode 100644 index 00000000..da4144e9 --- /dev/null +++ b/jslib/common/src/models/request/keysRequest.ts @@ -0,0 +1,9 @@ +export class KeysRequest { + publicKey: string; + encryptedPrivateKey: string; + + constructor(publicKey: string, encryptedPrivateKey: string) { + this.publicKey = publicKey; + this.encryptedPrivateKey = encryptedPrivateKey; + } +} diff --git a/jslib/common/src/models/request/kvpRequest.ts b/jslib/common/src/models/request/kvpRequest.ts new file mode 100644 index 00000000..ca37a85d --- /dev/null +++ b/jslib/common/src/models/request/kvpRequest.ts @@ -0,0 +1,9 @@ +export class KvpRequest { + key: TK; + value: TV; + + constructor(key: TK, value: TV) { + this.key = key; + this.value = value; + } +} diff --git a/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts b/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts new file mode 100644 index 00000000..7cc854b6 --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSponsorshipCreateRequest.ts @@ -0,0 +1,7 @@ +import { PlanSponsorshipType } from "../../../enums/planSponsorshipType"; + +export class OrganizationSponsorshipCreateRequest { + sponsoredEmail: string; + planSponsorshipType: PlanSponsorshipType; + friendlyName: string; +} diff --git a/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts b/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts new file mode 100644 index 00000000..4c73836e --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSponsorshipRedeemRequest.ts @@ -0,0 +1,6 @@ +import { PlanSponsorshipType } from "../../../enums/planSponsorshipType"; + +export class OrganizationSponsorshipRedeemRequest { + planSponsorshipType: PlanSponsorshipType; + sponsoredOrganizationId: string; +} diff --git a/jslib/common/src/models/request/organization/organizationSsoRequest.ts b/jslib/common/src/models/request/organization/organizationSsoRequest.ts new file mode 100644 index 00000000..e7fdb61c --- /dev/null +++ b/jslib/common/src/models/request/organization/organizationSsoRequest.ts @@ -0,0 +1,6 @@ +import { SsoConfigApi } from "../../api/ssoConfigApi"; + +export class OrganizationSsoRequest { + enabled = false; + data: SsoConfigApi; +} diff --git a/jslib/common/src/models/request/organizationCreateRequest.ts b/jslib/common/src/models/request/organizationCreateRequest.ts new file mode 100644 index 00000000..db713d90 --- /dev/null +++ b/jslib/common/src/models/request/organizationCreateRequest.ts @@ -0,0 +1,27 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { PlanType } from "../../enums/planType"; + +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationCreateRequest { + name: string; + businessName: string; + billingEmail: string; + planType: PlanType; + key: string; + keys: OrganizationKeysRequest; + paymentMethodType: PaymentMethodType; + paymentToken: string; + additionalSeats: number; + maxAutoscaleSeats: number; + additionalStorageGb: number; + premiumAccessAddon: boolean; + collectionName: string; + taxIdNumber: string; + billingAddressLine1: string; + billingAddressLine2: string; + billingAddressCity: string; + billingAddressState: string; + billingAddressPostalCode: string; + billingAddressCountry: string; +} diff --git a/jslib/common/src/models/request/organizationImportGroupRequest.ts b/jslib/common/src/models/request/organizationImportGroupRequest.ts new file mode 100644 index 00000000..086ce653 --- /dev/null +++ b/jslib/common/src/models/request/organizationImportGroupRequest.ts @@ -0,0 +1,18 @@ +import { ImportDirectoryRequestGroup } from "./importDirectoryRequestGroup"; + +export class OrganizationImportGroupRequest { + name: string; + externalId: string; + memberExternalIds: string[]; + + constructor(model: Required | ImportDirectoryRequestGroup) { + this.name = model.name; + this.externalId = model.externalId; + + if (model instanceof ImportDirectoryRequestGroup) { + this.memberExternalIds = model.users; + } else { + this.memberExternalIds = model.memberExternalIds; + } + } +} diff --git a/jslib/common/src/models/request/organizationImportMemberRequest.ts b/jslib/common/src/models/request/organizationImportMemberRequest.ts new file mode 100644 index 00000000..8161365c --- /dev/null +++ b/jslib/common/src/models/request/organizationImportMemberRequest.ts @@ -0,0 +1,13 @@ +import { ImportDirectoryRequestUser } from "./importDirectoryRequestUser"; + +export class OrganizationImportMemberRequest { + email: string; + externalId: string; + deleted: boolean; + + constructor(model: Required | ImportDirectoryRequestUser) { + this.email = model.email; + this.externalId = model.externalId; + this.deleted = model.deleted; + } +} diff --git a/jslib/common/src/models/request/organizationImportRequest.ts b/jslib/common/src/models/request/organizationImportRequest.ts new file mode 100644 index 00000000..af7db2be --- /dev/null +++ b/jslib/common/src/models/request/organizationImportRequest.ts @@ -0,0 +1,31 @@ +import { ImportDirectoryRequest } from "./importDirectoryRequest"; +import { OrganizationImportGroupRequest } from "./organizationImportGroupRequest"; +import { OrganizationImportMemberRequest } from "./organizationImportMemberRequest"; + +export class OrganizationImportRequest { + groups: OrganizationImportGroupRequest[] = []; + members: OrganizationImportMemberRequest[] = []; + overwriteExisting = false; + largeImport = false; + + constructor( + model: + | { + groups: Required[]; + users: Required[]; + overwriteExisting: boolean; + largeImport: boolean; + } + | ImportDirectoryRequest + ) { + if (model instanceof ImportDirectoryRequest) { + this.groups = model.groups.map((g) => new OrganizationImportGroupRequest(g)); + this.members = model.users.map((u) => new OrganizationImportMemberRequest(u)); + } else { + this.groups = model.groups.map((g) => new OrganizationImportGroupRequest(g)); + this.members = model.users.map((u) => new OrganizationImportMemberRequest(u)); + } + this.overwriteExisting = model.overwriteExisting; + this.largeImport = model.largeImport; + } +} diff --git a/jslib/common/src/models/request/organizationKeysRequest.ts b/jslib/common/src/models/request/organizationKeysRequest.ts new file mode 100644 index 00000000..c63e05ab --- /dev/null +++ b/jslib/common/src/models/request/organizationKeysRequest.ts @@ -0,0 +1,7 @@ +import { KeysRequest } from "./keysRequest"; + +export class OrganizationKeysRequest extends KeysRequest { + constructor(publicKey: string, encryptedPrivateKey: string) { + super(publicKey, encryptedPrivateKey); + } +} diff --git a/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts b/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts new file mode 100644 index 00000000..9db3d4be --- /dev/null +++ b/jslib/common/src/models/request/organizationSubscriptionUpdateRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationSubscriptionUpdateRequest { + constructor(public seatAdjustment: number, public maxAutoscaleSeats?: number) {} +} diff --git a/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts b/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts new file mode 100644 index 00000000..283151f4 --- /dev/null +++ b/jslib/common/src/models/request/organizationTaxInfoUpdateRequest.ts @@ -0,0 +1,9 @@ +import { TaxInfoUpdateRequest } from "./taxInfoUpdateRequest"; + +export class OrganizationTaxInfoUpdateRequest extends TaxInfoUpdateRequest { + taxId: string; + line1: string; + line2: string; + city: string; + state: string; +} diff --git a/jslib/common/src/models/request/organizationUpdateRequest.ts b/jslib/common/src/models/request/organizationUpdateRequest.ts new file mode 100644 index 00000000..faddf641 --- /dev/null +++ b/jslib/common/src/models/request/organizationUpdateRequest.ts @@ -0,0 +1,9 @@ +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationUpdateRequest { + name: string; + identifier: string; + businessName: string; + billingEmail: string; + keys: OrganizationKeysRequest; +} diff --git a/jslib/common/src/models/request/organizationUpgradeRequest.ts b/jslib/common/src/models/request/organizationUpgradeRequest.ts new file mode 100644 index 00000000..b62976fa --- /dev/null +++ b/jslib/common/src/models/request/organizationUpgradeRequest.ts @@ -0,0 +1,14 @@ +import { PlanType } from "../../enums/planType"; + +import { OrganizationKeysRequest } from "./organizationKeysRequest"; + +export class OrganizationUpgradeRequest { + businessName: string; + planType: PlanType; + additionalSeats: number; + additionalStorageGb: number; + premiumAccessAddon: boolean; + billingAddressCountry: string; + billingAddressPostalCode: string; + keys: OrganizationKeysRequest; +} diff --git a/jslib/common/src/models/request/organizationUserAcceptRequest.ts b/jslib/common/src/models/request/organizationUserAcceptRequest.ts new file mode 100644 index 00000000..c4b2a4d3 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserAcceptRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts b/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts new file mode 100644 index 00000000..35e05602 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserBulkConfirmRequest.ts @@ -0,0 +1,12 @@ +type OrganizationUserBulkRequestEntry = { + id: string; + key: string; +}; + +export class OrganizationUserBulkConfirmRequest { + keys: OrganizationUserBulkRequestEntry[]; + + constructor(keys: OrganizationUserBulkRequestEntry[]) { + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/organizationUserBulkRequest.ts b/jslib/common/src/models/request/organizationUserBulkRequest.ts new file mode 100644 index 00000000..c73800eb --- /dev/null +++ b/jslib/common/src/models/request/organizationUserBulkRequest.ts @@ -0,0 +1,7 @@ +export class OrganizationUserBulkRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/organizationUserConfirmRequest.ts b/jslib/common/src/models/request/organizationUserConfirmRequest.ts new file mode 100644 index 00000000..abd48749 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserConfirmRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/organizationUserInviteRequest.ts b/jslib/common/src/models/request/organizationUserInviteRequest.ts new file mode 100644 index 00000000..7d037004 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserInviteRequest.ts @@ -0,0 +1,12 @@ +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class OrganizationUserInviteRequest { + emails: string[] = []; + type: OrganizationUserType; + accessAll: boolean; + collections: SelectionReadOnlyRequest[] = []; + permissions: PermissionsApi; +} diff --git a/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts b/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts new file mode 100644 index 00000000..8d88164d --- /dev/null +++ b/jslib/common/src/models/request/organizationUserResetPasswordEnrollmentRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserResetPasswordEnrollmentRequest { + resetPasswordKey: string; +} diff --git a/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts b/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts new file mode 100644 index 00000000..b0c4e483 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserResetPasswordRequest.ts @@ -0,0 +1,4 @@ +export class OrganizationUserResetPasswordRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts b/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts new file mode 100644 index 00000000..cd30d940 --- /dev/null +++ b/jslib/common/src/models/request/organizationUserUpdateGroupsRequest.ts @@ -0,0 +1,3 @@ +export class OrganizationUserUpdateGroupsRequest { + groupIds: string[] = []; +} diff --git a/jslib/common/src/models/request/organizationUserUpdateRequest.ts b/jslib/common/src/models/request/organizationUserUpdateRequest.ts new file mode 100644 index 00000000..ec9ae3bc --- /dev/null +++ b/jslib/common/src/models/request/organizationUserUpdateRequest.ts @@ -0,0 +1,11 @@ +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { SelectionReadOnlyRequest } from "./selectionReadOnlyRequest"; + +export class OrganizationUserUpdateRequest { + type: OrganizationUserType; + accessAll: boolean; + collections: SelectionReadOnlyRequest[] = []; + permissions: PermissionsApi; +} diff --git a/jslib/common/src/models/request/passwordHintRequest.ts b/jslib/common/src/models/request/passwordHintRequest.ts new file mode 100644 index 00000000..7182e05e --- /dev/null +++ b/jslib/common/src/models/request/passwordHintRequest.ts @@ -0,0 +1,7 @@ +export class PasswordHintRequest { + email: string; + + constructor(email: string) { + this.email = email; + } +} diff --git a/jslib/common/src/models/request/passwordHistoryRequest.ts b/jslib/common/src/models/request/passwordHistoryRequest.ts new file mode 100644 index 00000000..6cca2b86 --- /dev/null +++ b/jslib/common/src/models/request/passwordHistoryRequest.ts @@ -0,0 +1,4 @@ +export class PasswordHistoryRequest { + password: string; + lastUsedDate: Date; +} diff --git a/jslib/common/src/models/request/passwordRequest.ts b/jslib/common/src/models/request/passwordRequest.ts new file mode 100644 index 00000000..9f7df7df --- /dev/null +++ b/jslib/common/src/models/request/passwordRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class PasswordRequest extends SecretVerificationRequest { + newMasterPasswordHash: string; + key: string; +} diff --git a/jslib/common/src/models/request/paymentRequest.ts b/jslib/common/src/models/request/paymentRequest.ts new file mode 100644 index 00000000..8e7f70a1 --- /dev/null +++ b/jslib/common/src/models/request/paymentRequest.ts @@ -0,0 +1,7 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { OrganizationTaxInfoUpdateRequest } from "../request/organizationTaxInfoUpdateRequest"; + +export class PaymentRequest extends OrganizationTaxInfoUpdateRequest { + paymentMethodType: PaymentMethodType; + paymentToken: string; +} diff --git a/jslib/common/src/models/request/policyRequest.ts b/jslib/common/src/models/request/policyRequest.ts new file mode 100644 index 00000000..98b05912 --- /dev/null +++ b/jslib/common/src/models/request/policyRequest.ts @@ -0,0 +1,7 @@ +import { PolicyType } from "../../enums/policyType"; + +export class PolicyRequest { + type: PolicyType; + enabled: boolean; + data: any; +} diff --git a/jslib/common/src/models/request/preloginRequest.ts b/jslib/common/src/models/request/preloginRequest.ts new file mode 100644 index 00000000..689204b7 --- /dev/null +++ b/jslib/common/src/models/request/preloginRequest.ts @@ -0,0 +1,7 @@ +export class PreloginRequest { + email: string; + + constructor(email: string) { + this.email = email; + } +} diff --git a/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts b/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts new file mode 100644 index 00000000..380eea1d --- /dev/null +++ b/jslib/common/src/models/request/provider/providerAddOrganizationRequest.ts @@ -0,0 +1,4 @@ +export class ProviderAddOrganizationRequest { + organizationId: string; + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts b/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts new file mode 100644 index 00000000..8d02f530 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerOrganizationCreateRequest.ts @@ -0,0 +1,8 @@ +import { OrganizationCreateRequest } from "../organizationCreateRequest"; + +export class ProviderOrganizationCreateRequest { + constructor( + public clientOwnerEmail: string, + public organizationCreateRequest: OrganizationCreateRequest + ) {} +} diff --git a/jslib/common/src/models/request/provider/providerSetupRequest.ts b/jslib/common/src/models/request/provider/providerSetupRequest.ts new file mode 100644 index 00000000..61eb943f --- /dev/null +++ b/jslib/common/src/models/request/provider/providerSetupRequest.ts @@ -0,0 +1,7 @@ +export class ProviderSetupRequest { + name: string; + businessName: string; + billingEmail: string; + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerUpdateRequest.ts b/jslib/common/src/models/request/provider/providerUpdateRequest.ts new file mode 100644 index 00000000..dafa7418 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUpdateRequest.ts @@ -0,0 +1,5 @@ +export class ProviderUpdateRequest { + name: string; + businessName: string; + billingEmail: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts b/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts new file mode 100644 index 00000000..0435e1df --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserAcceptRequest.ts @@ -0,0 +1,3 @@ +export class ProviderUserAcceptRequest { + token: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts b/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts new file mode 100644 index 00000000..76628b09 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserBulkConfirmRequest.ts @@ -0,0 +1,12 @@ +type ProviderUserBulkRequestEntry = { + id: string; + key: string; +}; + +export class ProviderUserBulkConfirmRequest { + keys: ProviderUserBulkRequestEntry[]; + + constructor(keys: ProviderUserBulkRequestEntry[]) { + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/provider/providerUserBulkRequest.ts b/jslib/common/src/models/request/provider/providerUserBulkRequest.ts new file mode 100644 index 00000000..f45ed1bb --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserBulkRequest.ts @@ -0,0 +1,7 @@ +export class ProviderUserBulkRequest { + ids: string[]; + + constructor(ids: string[]) { + this.ids = ids == null ? [] : ids; + } +} diff --git a/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts b/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts new file mode 100644 index 00000000..1b7d4a06 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserConfirmRequest.ts @@ -0,0 +1,3 @@ +export class ProviderUserConfirmRequest { + key: string; +} diff --git a/jslib/common/src/models/request/provider/providerUserInviteRequest.ts b/jslib/common/src/models/request/provider/providerUserInviteRequest.ts new file mode 100644 index 00000000..65d8ba67 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserInviteRequest.ts @@ -0,0 +1,6 @@ +import { ProviderUserType } from "../../../enums/providerUserType"; + +export class ProviderUserInviteRequest { + emails: string[] = []; + type: ProviderUserType; +} diff --git a/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts b/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts new file mode 100644 index 00000000..25efdd89 --- /dev/null +++ b/jslib/common/src/models/request/provider/providerUserUpdateRequest.ts @@ -0,0 +1,5 @@ +import { ProviderUserType } from "../../../enums/providerUserType"; + +export class ProviderUserUpdateRequest { + type: ProviderUserType; +} diff --git a/jslib/common/src/models/request/referenceEventRequest.ts b/jslib/common/src/models/request/referenceEventRequest.ts new file mode 100644 index 00000000..7a0b535a --- /dev/null +++ b/jslib/common/src/models/request/referenceEventRequest.ts @@ -0,0 +1,5 @@ +export class ReferenceEventRequest { + id: string; + layout: string; + flow: string; +} diff --git a/jslib/common/src/models/request/registerRequest.ts b/jslib/common/src/models/request/registerRequest.ts new file mode 100644 index 00000000..8650c5c5 --- /dev/null +++ b/jslib/common/src/models/request/registerRequest.ts @@ -0,0 +1,26 @@ +import { KdfType } from "../../enums/kdfType"; + +import { CaptchaProtectedRequest } from "./captchaProtectedRequest"; +import { KeysRequest } from "./keysRequest"; +import { ReferenceEventRequest } from "./referenceEventRequest"; + +export class RegisterRequest implements CaptchaProtectedRequest { + masterPasswordHint: string; + keys: KeysRequest; + token: string; + organizationUserId: string; + + constructor( + public email: string, + public name: string, + public masterPasswordHash: string, + masterPasswordHint: string, + public key: string, + public kdf: KdfType, + public kdfIterations: number, + public referenceData: ReferenceEventRequest, + public captchaResponse: string + ) { + this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null; + } +} diff --git a/jslib/common/src/models/request/seatRequest.ts b/jslib/common/src/models/request/seatRequest.ts new file mode 100644 index 00000000..d60e41fa --- /dev/null +++ b/jslib/common/src/models/request/seatRequest.ts @@ -0,0 +1,3 @@ +export class SeatRequest { + seatAdjustment: number; +} diff --git a/jslib/common/src/models/request/secretVerificationRequest.ts b/jslib/common/src/models/request/secretVerificationRequest.ts new file mode 100644 index 00000000..c0170a3e --- /dev/null +++ b/jslib/common/src/models/request/secretVerificationRequest.ts @@ -0,0 +1,4 @@ +export class SecretVerificationRequest { + masterPasswordHash: string; + otp: string; +} diff --git a/jslib/common/src/models/request/selectionReadOnlyRequest.ts b/jslib/common/src/models/request/selectionReadOnlyRequest.ts new file mode 100644 index 00000000..7b007324 --- /dev/null +++ b/jslib/common/src/models/request/selectionReadOnlyRequest.ts @@ -0,0 +1,11 @@ +export class SelectionReadOnlyRequest { + id: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(id: string, readOnly: boolean, hidePasswords: boolean) { + this.id = id; + this.readOnly = readOnly; + this.hidePasswords = hidePasswords; + } +} diff --git a/jslib/common/src/models/request/sendAccessRequest.ts b/jslib/common/src/models/request/sendAccessRequest.ts new file mode 100644 index 00000000..7607b03c --- /dev/null +++ b/jslib/common/src/models/request/sendAccessRequest.ts @@ -0,0 +1,3 @@ +export class SendAccessRequest { + password: string; +} diff --git a/jslib/common/src/models/request/sendRequest.ts b/jslib/common/src/models/request/sendRequest.ts new file mode 100644 index 00000000..1c77204c --- /dev/null +++ b/jslib/common/src/models/request/sendRequest.ts @@ -0,0 +1,48 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; +import { Send } from "../domain/send"; + +export class SendRequest { + type: SendType; + fileLength?: number; + name: string; + notes: string; + key: string; + maxAccessCount?: number; + expirationDate: string; + deletionDate: string; + text: SendTextApi; + file: SendFileApi; + password: string; + disabled: boolean; + hideEmail: boolean; + + constructor(send: Send, fileLength?: number) { + this.type = send.type; + this.fileLength = fileLength; + this.name = send.name ? send.name.encryptedString : null; + this.notes = send.notes ? send.notes.encryptedString : null; + this.maxAccessCount = send.maxAccessCount; + this.expirationDate = send.expirationDate != null ? send.expirationDate.toISOString() : null; + this.deletionDate = send.deletionDate != null ? send.deletionDate.toISOString() : null; + this.key = send.key != null ? send.key.encryptedString : null; + this.password = send.password; + this.disabled = send.disabled; + this.hideEmail = send.hideEmail; + + switch (this.type) { + case SendType.Text: + this.text = new SendTextApi(); + this.text.text = send.text.text != null ? send.text.text.encryptedString : null; + this.text.hidden = send.text.hidden; + break; + case SendType.File: + this.file = new SendFileApi(); + this.file.fileName = send.file.fileName != null ? send.file.fileName.encryptedString : null; + break; + default: + break; + } + } +} diff --git a/jslib/common/src/models/request/sendWithIdRequest.ts b/jslib/common/src/models/request/sendWithIdRequest.ts new file mode 100644 index 00000000..8591a57e --- /dev/null +++ b/jslib/common/src/models/request/sendWithIdRequest.ts @@ -0,0 +1,12 @@ +import { Send } from "../domain/send"; + +import { SendRequest } from "./sendRequest"; + +export class SendWithIdRequest extends SendRequest { + id: string; + + constructor(send: Send) { + super(send); + this.id = send.id; + } +} diff --git a/jslib/common/src/models/request/setPasswordRequest.ts b/jslib/common/src/models/request/setPasswordRequest.ts new file mode 100644 index 00000000..919a132f --- /dev/null +++ b/jslib/common/src/models/request/setPasswordRequest.ts @@ -0,0 +1,31 @@ +import { KdfType } from "../../enums/kdfType"; + +import { KeysRequest } from "./keysRequest"; + +export class SetPasswordRequest { + masterPasswordHash: string; + key: string; + masterPasswordHint: string; + keys: KeysRequest; + kdf: KdfType; + kdfIterations: number; + orgIdentifier: string; + + constructor( + masterPasswordHash: string, + key: string, + masterPasswordHint: string, + kdf: KdfType, + kdfIterations: number, + orgIdentifier: string, + keys: KeysRequest + ) { + this.masterPasswordHash = masterPasswordHash; + this.key = key; + this.masterPasswordHint = masterPasswordHint; + this.kdf = kdf; + this.kdfIterations = kdfIterations; + this.orgIdentifier = orgIdentifier; + this.keys = keys; + } +} diff --git a/jslib/common/src/models/request/storageRequest.ts b/jslib/common/src/models/request/storageRequest.ts new file mode 100644 index 00000000..4b3b614d --- /dev/null +++ b/jslib/common/src/models/request/storageRequest.ts @@ -0,0 +1,3 @@ +export class StorageRequest { + storageGbAdjustment: number; +} diff --git a/jslib/common/src/models/request/taxInfoUpdateRequest.ts b/jslib/common/src/models/request/taxInfoUpdateRequest.ts new file mode 100644 index 00000000..6881a8a4 --- /dev/null +++ b/jslib/common/src/models/request/taxInfoUpdateRequest.ts @@ -0,0 +1,4 @@ +export class TaxInfoUpdateRequest { + country: string; + postalCode: string; +} diff --git a/jslib/common/src/models/request/twoFactorEmailRequest.ts b/jslib/common/src/models/request/twoFactorEmailRequest.ts new file mode 100644 index 00000000..36c168b6 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorEmailRequest.ts @@ -0,0 +1,5 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorEmailRequest extends SecretVerificationRequest { + email: string; +} diff --git a/jslib/common/src/models/request/twoFactorProviderRequest.ts b/jslib/common/src/models/request/twoFactorProviderRequest.ts new file mode 100644 index 00000000..9d606bc9 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorProviderRequest.ts @@ -0,0 +1,7 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorProviderRequest extends SecretVerificationRequest { + type: TwoFactorProviderType; +} diff --git a/jslib/common/src/models/request/twoFactorRecoveryRequest.ts b/jslib/common/src/models/request/twoFactorRecoveryRequest.ts new file mode 100644 index 00000000..39135a37 --- /dev/null +++ b/jslib/common/src/models/request/twoFactorRecoveryRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class TwoFactorRecoveryRequest extends SecretVerificationRequest { + recoveryCode: string; + email: string; +} diff --git a/jslib/common/src/models/request/updateDomainsRequest.ts b/jslib/common/src/models/request/updateDomainsRequest.ts new file mode 100644 index 00000000..528d3682 --- /dev/null +++ b/jslib/common/src/models/request/updateDomainsRequest.ts @@ -0,0 +1,4 @@ +export class UpdateDomainsRequest { + equivalentDomains: string[][]; + excludedGlobalEquivalentDomains: number[]; +} diff --git a/jslib/common/src/models/request/updateKeyRequest.ts b/jslib/common/src/models/request/updateKeyRequest.ts new file mode 100644 index 00000000..a9fceffd --- /dev/null +++ b/jslib/common/src/models/request/updateKeyRequest.ts @@ -0,0 +1,12 @@ +import { CipherWithIdRequest } from "./cipherWithIdRequest"; +import { FolderWithIdRequest } from "./folderWithIdRequest"; +import { SendWithIdRequest } from "./sendWithIdRequest"; + +export class UpdateKeyRequest { + ciphers: CipherWithIdRequest[] = []; + folders: FolderWithIdRequest[] = []; + sends: SendWithIdRequest[] = []; + masterPasswordHash: string; + privateKey: string; + key: string; +} diff --git a/jslib/common/src/models/request/updateProfileRequest.ts b/jslib/common/src/models/request/updateProfileRequest.ts new file mode 100644 index 00000000..14ed554d --- /dev/null +++ b/jslib/common/src/models/request/updateProfileRequest.ts @@ -0,0 +1,10 @@ +export class UpdateProfileRequest { + name: string; + masterPasswordHint: string; + culture = "en-US"; // deprecated + + constructor(name: string, masterPasswordHint: string) { + this.name = name; + this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null; + } +} diff --git a/jslib/common/src/models/request/updateTempPasswordRequest.ts b/jslib/common/src/models/request/updateTempPasswordRequest.ts new file mode 100644 index 00000000..1bd97697 --- /dev/null +++ b/jslib/common/src/models/request/updateTempPasswordRequest.ts @@ -0,0 +1,5 @@ +import { OrganizationUserResetPasswordRequest } from "./organizationUserResetPasswordRequest"; + +export class UpdateTempPasswordRequest extends OrganizationUserResetPasswordRequest { + masterPasswordHint: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts b/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts new file mode 100644 index 00000000..9b718287 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorAuthenticatorRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorAuthenticatorRequest extends SecretVerificationRequest { + token: string; + key: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts b/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts new file mode 100644 index 00000000..cd82215d --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorDuoRequest.ts @@ -0,0 +1,7 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorDuoRequest extends SecretVerificationRequest { + integrationKey: string; + secretKey: string; + host: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts b/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts new file mode 100644 index 00000000..be7da1fd --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorEmailRequest.ts @@ -0,0 +1,6 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorEmailRequest extends SecretVerificationRequest { + token: string; + email: string; +} diff --git a/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts b/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts new file mode 100644 index 00000000..1decda72 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorWebAuthnDeleteRequest.ts @@ -0,0 +1,5 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorWebAuthnDeleteRequest extends SecretVerificationRequest { + id: number; +} diff --git a/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts b/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts new file mode 100644 index 00000000..2e788b52 --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorWebAuthnRequest.ts @@ -0,0 +1,7 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorWebAuthnRequest extends SecretVerificationRequest { + deviceResponse: PublicKeyCredential; + name: string; + id: number; +} diff --git a/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts b/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts new file mode 100644 index 00000000..cb495b1e --- /dev/null +++ b/jslib/common/src/models/request/updateTwoFactorYubioOtpRequest.ts @@ -0,0 +1,10 @@ +import { SecretVerificationRequest } from "./secretVerificationRequest"; + +export class UpdateTwoFactorYubioOtpRequest extends SecretVerificationRequest { + key1: string; + key2: string; + key3: string; + key4: string; + key5: string; + nfc: boolean; +} diff --git a/jslib/common/src/models/request/verifyBankRequest.ts b/jslib/common/src/models/request/verifyBankRequest.ts new file mode 100644 index 00000000..823eaf46 --- /dev/null +++ b/jslib/common/src/models/request/verifyBankRequest.ts @@ -0,0 +1,4 @@ +export class VerifyBankRequest { + amount1: number; + amount2: number; +} diff --git a/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts b/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts new file mode 100644 index 00000000..2374d32d --- /dev/null +++ b/jslib/common/src/models/request/verifyDeleteRecoverRequest.ts @@ -0,0 +1,9 @@ +export class VerifyDeleteRecoverRequest { + userId: string; + token: string; + + constructor(userId: string, token: string) { + this.userId = userId; + this.token = token; + } +} diff --git a/jslib/common/src/models/request/verifyEmailRequest.ts b/jslib/common/src/models/request/verifyEmailRequest.ts new file mode 100644 index 00000000..ecee0190 --- /dev/null +++ b/jslib/common/src/models/request/verifyEmailRequest.ts @@ -0,0 +1,9 @@ +export class VerifyEmailRequest { + userId: string; + token: string; + + constructor(userId: string, token: string) { + this.userId = userId; + this.token = token; + } +} diff --git a/jslib/common/src/models/response/apiKeyResponse.ts b/jslib/common/src/models/response/apiKeyResponse.ts new file mode 100644 index 00000000..f1a24be0 --- /dev/null +++ b/jslib/common/src/models/response/apiKeyResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class ApiKeyResponse extends BaseResponse { + apiKey: string; + + constructor(response: any) { + super(response); + this.apiKey = this.getResponseProperty("ApiKey"); + } +} diff --git a/jslib/common/src/models/response/attachmentResponse.ts b/jslib/common/src/models/response/attachmentResponse.ts new file mode 100644 index 00000000..a9ebfaf3 --- /dev/null +++ b/jslib/common/src/models/response/attachmentResponse.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "./baseResponse"; + +export class AttachmentResponse extends BaseResponse { + id: string; + url: string; + fileName: string; + key: string; + size: string; + sizeName: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.url = this.getResponseProperty("Url"); + this.fileName = this.getResponseProperty("FileName"); + this.key = this.getResponseProperty("Key"); + this.size = this.getResponseProperty("Size"); + this.sizeName = this.getResponseProperty("SizeName"); + } +} diff --git a/jslib/common/src/models/response/attachmentUploadDataResponse.ts b/jslib/common/src/models/response/attachmentUploadDataResponse.ts new file mode 100644 index 00000000..4520c309 --- /dev/null +++ b/jslib/common/src/models/response/attachmentUploadDataResponse.ts @@ -0,0 +1,23 @@ +import { FileUploadType } from "../../enums/fileUploadType"; + +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; + +export class AttachmentUploadDataResponse extends BaseResponse { + attachmentId: string; + fileUploadType: FileUploadType; + cipherResponse: CipherResponse; + cipherMiniResponse: CipherResponse; + url: string = null; + constructor(response: any) { + super(response); + this.attachmentId = this.getResponseProperty("AttachmentId"); + this.fileUploadType = this.getResponseProperty("FileUploadType"); + const cipherResponse = this.getResponseProperty("CipherResponse"); + const cipherMiniResponse = this.getResponseProperty("CipherMiniResponse"); + this.cipherResponse = cipherResponse == null ? null : new CipherResponse(cipherResponse); + this.cipherMiniResponse = + cipherMiniResponse == null ? null : new CipherResponse(cipherMiniResponse); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/baseResponse.ts b/jslib/common/src/models/response/baseResponse.ts new file mode 100644 index 00000000..8c599fba --- /dev/null +++ b/jslib/common/src/models/response/baseResponse.ts @@ -0,0 +1,43 @@ +export abstract class BaseResponse { + private response: any; + + constructor(response: any) { + this.response = response; + } + + protected getResponseProperty( + propertyName: string, + response: any = null, + exactName = false + ): any { + if (propertyName == null || propertyName === "") { + throw new Error("propertyName must not be null/empty."); + } + if (response == null && this.response != null) { + response = this.response; + } + if (response == null) { + return null; + } + if (!exactName && response[propertyName] === undefined) { + let otherCasePropertyName: string = null; + if (propertyName.charAt(0) === propertyName.charAt(0).toUpperCase()) { + otherCasePropertyName = propertyName.charAt(0).toLowerCase(); + } else { + otherCasePropertyName = propertyName.charAt(0).toUpperCase(); + } + if (propertyName.length > 1) { + otherCasePropertyName += propertyName.slice(1); + } + + propertyName = otherCasePropertyName; + if (response[propertyName] === undefined) { + propertyName = propertyName.toLowerCase(); + } + if (response[propertyName] === undefined) { + propertyName = propertyName.toUpperCase(); + } + } + return response[propertyName]; + } +} diff --git a/jslib/common/src/models/response/billingResponse.ts b/jslib/common/src/models/response/billingResponse.ts new file mode 100644 index 00000000..8d09cf93 --- /dev/null +++ b/jslib/common/src/models/response/billingResponse.ts @@ -0,0 +1,83 @@ +import { PaymentMethodType } from "../../enums/paymentMethodType"; +import { TransactionType } from "../../enums/transactionType"; + +import { BaseResponse } from "./baseResponse"; + +export class BillingResponse extends BaseResponse { + balance: number; + paymentSource: BillingSourceResponse; + invoices: BillingInvoiceResponse[] = []; + transactions: BillingTransactionResponse[] = []; + + constructor(response: any) { + super(response); + this.balance = this.getResponseProperty("Balance"); + const paymentSource = this.getResponseProperty("PaymentSource"); + const transactions = this.getResponseProperty("Transactions"); + const invoices = this.getResponseProperty("Invoices"); + this.paymentSource = paymentSource == null ? null : new BillingSourceResponse(paymentSource); + if (transactions != null) { + this.transactions = transactions.map((t: any) => new BillingTransactionResponse(t)); + } + if (invoices != null) { + this.invoices = invoices.map((i: any) => new BillingInvoiceResponse(i)); + } + } +} + +export class BillingSourceResponse extends BaseResponse { + type: PaymentMethodType; + cardBrand: string; + description: string; + needsVerification: boolean; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.cardBrand = this.getResponseProperty("CardBrand"); + this.description = this.getResponseProperty("Description"); + this.needsVerification = this.getResponseProperty("NeedsVerification"); + } +} + +export class BillingInvoiceResponse extends BaseResponse { + url: string; + pdfUrl: string; + number: string; + paid: boolean; + date: string; + amount: number; + + constructor(response: any) { + super(response); + this.url = this.getResponseProperty("Url"); + this.pdfUrl = this.getResponseProperty("PdfUrl"); + this.number = this.getResponseProperty("Number"); + this.paid = this.getResponseProperty("Paid"); + this.date = this.getResponseProperty("Date"); + this.amount = this.getResponseProperty("Amount"); + } +} + +export class BillingTransactionResponse extends BaseResponse { + createdDate: string; + amount: number; + refunded: boolean; + partiallyRefunded: boolean; + refundedAmount: number; + type: TransactionType; + paymentMethodType: PaymentMethodType; + details: string; + + constructor(response: any) { + super(response); + this.createdDate = this.getResponseProperty("CreatedDate"); + this.amount = this.getResponseProperty("Amount"); + this.refunded = this.getResponseProperty("Refunded"); + this.partiallyRefunded = this.getResponseProperty("PartiallyRefunded"); + this.refundedAmount = this.getResponseProperty("RefundedAmount"); + this.type = this.getResponseProperty("Type"); + this.paymentMethodType = this.getResponseProperty("PaymentMethodType"); + this.details = this.getResponseProperty("Details"); + } +} diff --git a/jslib/common/src/models/response/breachAccountResponse.ts b/jslib/common/src/models/response/breachAccountResponse.ts new file mode 100644 index 00000000..00d39acc --- /dev/null +++ b/jslib/common/src/models/response/breachAccountResponse.ts @@ -0,0 +1,32 @@ +import { BaseResponse } from "./baseResponse"; + +export class BreachAccountResponse extends BaseResponse { + addedDate: string; + breachDate: string; + dataClasses: string[]; + description: string; + domain: string; + isActive: boolean; + isVerified: boolean; + logoPath: string; + modifiedDate: string; + name: string; + pwnCount: number; + title: string; + + constructor(response: any) { + super(response); + this.addedDate = this.getResponseProperty("AddedDate"); + this.breachDate = this.getResponseProperty("BreachDate"); + this.dataClasses = this.getResponseProperty("DataClasses"); + this.description = this.getResponseProperty("Description"); + this.domain = this.getResponseProperty("Domain"); + this.isActive = this.getResponseProperty("IsActive"); + this.isVerified = this.getResponseProperty("IsVerified"); + this.logoPath = this.getResponseProperty("LogoPath"); + this.modifiedDate = this.getResponseProperty("ModifiedDate"); + this.name = this.getResponseProperty("Name"); + this.pwnCount = this.getResponseProperty("PwnCount"); + this.title = this.getResponseProperty("Title"); + } +} diff --git a/jslib/common/src/models/response/cipherResponse.ts b/jslib/common/src/models/response/cipherResponse.ts new file mode 100644 index 00000000..5b461438 --- /dev/null +++ b/jslib/common/src/models/response/cipherResponse.ts @@ -0,0 +1,92 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CardApi } from "../api/cardApi"; +import { FieldApi } from "../api/fieldApi"; +import { IdentityApi } from "../api/identityApi"; +import { LoginApi } from "../api/loginApi"; +import { SecureNoteApi } from "../api/secureNoteApi"; + +import { AttachmentResponse } from "./attachmentResponse"; +import { BaseResponse } from "./baseResponse"; +import { PasswordHistoryResponse } from "./passwordHistoryResponse"; + +export class CipherResponse extends BaseResponse { + id: string; + organizationId: string; + folderId: string; + type: number; + name: string; + notes: string; + fields: FieldApi[]; + login: LoginApi; + card: CardApi; + identity: IdentityApi; + secureNote: SecureNoteApi; + favorite: boolean; + edit: boolean; + viewPassword: boolean; + organizationUseTotp: boolean; + revisionDate: string; + attachments: AttachmentResponse[]; + passwordHistory: PasswordHistoryResponse[]; + collectionIds: string[]; + deletedDate: string; + reprompt: CipherRepromptType; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.folderId = this.getResponseProperty("FolderId") || null; + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.notes = this.getResponseProperty("Notes"); + this.favorite = this.getResponseProperty("Favorite") || false; + this.edit = !!this.getResponseProperty("Edit"); + if (this.getResponseProperty("ViewPassword") == null) { + this.viewPassword = true; + } else { + this.viewPassword = this.getResponseProperty("ViewPassword"); + } + this.organizationUseTotp = this.getResponseProperty("OrganizationUseTotp"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + this.collectionIds = this.getResponseProperty("CollectionIds"); + this.deletedDate = this.getResponseProperty("DeletedDate"); + + const login = this.getResponseProperty("Login"); + if (login != null) { + this.login = new LoginApi(login); + } + + const card = this.getResponseProperty("Card"); + if (card != null) { + this.card = new CardApi(card); + } + + const identity = this.getResponseProperty("Identity"); + if (identity != null) { + this.identity = new IdentityApi(identity); + } + + const secureNote = this.getResponseProperty("SecureNote"); + if (secureNote != null) { + this.secureNote = new SecureNoteApi(secureNote); + } + + const fields = this.getResponseProperty("Fields"); + if (fields != null) { + this.fields = fields.map((f: any) => new FieldApi(f)); + } + + const attachments = this.getResponseProperty("Attachments"); + if (attachments != null) { + this.attachments = attachments.map((a: any) => new AttachmentResponse(a)); + } + + const passwordHistory = this.getResponseProperty("PasswordHistory"); + if (passwordHistory != null) { + this.passwordHistory = passwordHistory.map((h: any) => new PasswordHistoryResponse(h)); + } + + this.reprompt = this.getResponseProperty("Reprompt") || CipherRepromptType.None; + } +} diff --git a/jslib/common/src/models/response/collectionResponse.ts b/jslib/common/src/models/response/collectionResponse.ts new file mode 100644 index 00000000..2f677f7d --- /dev/null +++ b/jslib/common/src/models/response/collectionResponse.ts @@ -0,0 +1,38 @@ +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class CollectionResponse extends BaseResponse { + id: string; + organizationId: string; + name: string; + externalId: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.name = this.getResponseProperty("Name"); + this.externalId = this.getResponseProperty("ExternalId"); + } +} + +export class CollectionDetailsResponse extends CollectionResponse { + readOnly: boolean; + + constructor(response: any) { + super(response); + this.readOnly = this.getResponseProperty("ReadOnly") || false; + } +} + +export class CollectionGroupDetailsResponse extends CollectionResponse { + groups: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const groups = this.getResponseProperty("Groups"); + if (groups != null) { + this.groups = groups.map((g: any) => new SelectionReadOnlyResponse(g)); + } + } +} diff --git a/jslib/common/src/models/response/deviceResponse.ts b/jslib/common/src/models/response/deviceResponse.ts new file mode 100644 index 00000000..aac9ac6c --- /dev/null +++ b/jslib/common/src/models/response/deviceResponse.ts @@ -0,0 +1,20 @@ +import { DeviceType } from "../../enums/deviceType"; + +import { BaseResponse } from "./baseResponse"; + +export class DeviceResponse extends BaseResponse { + id: string; + name: number; + identifier: string; + type: DeviceType; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.identifier = this.getResponseProperty("Identifier"); + this.type = this.getResponseProperty("Type"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} diff --git a/jslib/common/src/models/response/domainsResponse.ts b/jslib/common/src/models/response/domainsResponse.ts new file mode 100644 index 00000000..3014c2f2 --- /dev/null +++ b/jslib/common/src/models/response/domainsResponse.ts @@ -0,0 +1,20 @@ +import { BaseResponse } from "./baseResponse"; +import { GlobalDomainResponse } from "./globalDomainResponse"; + +export class DomainsResponse extends BaseResponse { + equivalentDomains: string[][]; + globalEquivalentDomains: GlobalDomainResponse[] = []; + + constructor(response: any) { + super(response); + this.equivalentDomains = this.getResponseProperty("EquivalentDomains"); + const globalEquivalentDomains = this.getResponseProperty("GlobalEquivalentDomains"); + if (globalEquivalentDomains != null) { + this.globalEquivalentDomains = globalEquivalentDomains.map( + (d: any) => new GlobalDomainResponse(d) + ); + } else { + this.globalEquivalentDomains = []; + } + } +} diff --git a/jslib/common/src/models/response/emergencyAccessResponse.ts b/jslib/common/src/models/response/emergencyAccessResponse.ts new file mode 100644 index 00000000..9ac5380d --- /dev/null +++ b/jslib/common/src/models/response/emergencyAccessResponse.ts @@ -0,0 +1,82 @@ +import { EmergencyAccessStatusType } from "../../enums/emergencyAccessStatusType"; +import { EmergencyAccessType } from "../../enums/emergencyAccessType"; +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; + +export class EmergencyAccessGranteeDetailsResponse extends BaseResponse { + id: string; + granteeId: string; + name: string; + email: string; + type: EmergencyAccessType; + status: EmergencyAccessStatusType; + waitTimeDays: number; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.granteeId = this.getResponseProperty("GranteeId"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.waitTimeDays = this.getResponseProperty("WaitTimeDays"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} + +export class EmergencyAccessGrantorDetailsResponse extends BaseResponse { + id: string; + grantorId: string; + name: string; + email: string; + type: EmergencyAccessType; + status: EmergencyAccessStatusType; + waitTimeDays: number; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.grantorId = this.getResponseProperty("GrantorId"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.waitTimeDays = this.getResponseProperty("WaitTimeDays"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} + +export class EmergencyAccessTakeoverResponse extends BaseResponse { + keyEncrypted: string; + kdf: KdfType; + kdfIterations: number; + + constructor(response: any) { + super(response); + + this.keyEncrypted = this.getResponseProperty("KeyEncrypted"); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + } +} + +export class EmergencyAccessViewResponse extends BaseResponse { + keyEncrypted: string; + ciphers: CipherResponse[] = []; + + constructor(response: any) { + super(response); + + this.keyEncrypted = this.getResponseProperty("KeyEncrypted"); + + const ciphers = this.getResponseProperty("Ciphers"); + if (ciphers != null) { + this.ciphers = ciphers.map((c: any) => new CipherResponse(c)); + } + } +} diff --git a/jslib/common/src/models/response/errorResponse.ts b/jslib/common/src/models/response/errorResponse.ts new file mode 100644 index 00000000..3a0d22e6 --- /dev/null +++ b/jslib/common/src/models/response/errorResponse.ts @@ -0,0 +1,74 @@ +import { Utils } from "../../misc/utils"; + +import { BaseResponse } from "./baseResponse"; + +export class ErrorResponse extends BaseResponse { + message: string; + validationErrors: { [key: string]: string[] }; + statusCode: number; + captchaRequired: boolean; + captchaSiteKey: string; + + constructor(response: any, status: number, identityResponse?: boolean) { + super(response); + let errorModel = null; + if (response != null) { + const responseErrorModel = this.getResponseProperty("ErrorModel"); + if (responseErrorModel && identityResponse) { + errorModel = responseErrorModel; + } else { + errorModel = response; + } + } + + if (errorModel) { + this.message = this.getResponseProperty("Message", errorModel); + this.validationErrors = this.getResponseProperty("ValidationErrors", errorModel); + this.captchaSiteKey = this.validationErrors?.HCaptcha_SiteKey?.[0]; + this.captchaRequired = !Utils.isNullOrWhitespace(this.captchaSiteKey); + } else { + if (status === 429) { + this.message = "Rate limit exceeded. Try again later."; + } + } + this.statusCode = status; + } + + getSingleMessage(): string { + if (this.validationErrors == null) { + return this.message; + } + for (const key in this.validationErrors) { + // eslint-disable-next-line + if (!this.validationErrors.hasOwnProperty(key)) { + continue; + } + if (this.validationErrors[key].length) { + return this.validationErrors[key][0]; + } + } + return this.message; + } + + getAllMessages(): string[] { + const messages: string[] = []; + if (this.validationErrors == null) { + return messages; + } + for (const key in this.validationErrors) { + // eslint-disable-next-line + if (!this.validationErrors.hasOwnProperty(key)) { + continue; + } + this.validationErrors[key].forEach((item: string) => { + let prefix = ""; + if (key.indexOf("[") > -1 && key.indexOf("]") > -1) { + const lastSep = key.lastIndexOf("."); + prefix = key.substr(0, lastSep > -1 ? lastSep : key.length) + ": "; + } + messages.push(prefix + item); + }); + } + return messages; + } +} diff --git a/jslib/common/src/models/response/eventResponse.ts b/jslib/common/src/models/response/eventResponse.ts new file mode 100644 index 00000000..93e31955 --- /dev/null +++ b/jslib/common/src/models/response/eventResponse.ts @@ -0,0 +1,41 @@ +import { DeviceType } from "../../enums/deviceType"; +import { EventType } from "../../enums/eventType"; + +import { BaseResponse } from "./baseResponse"; + +export class EventResponse extends BaseResponse { + type: EventType; + userId: string; + organizationId: string; + providerId: string; + cipherId: string; + collectionId: string; + groupId: string; + policyId: string; + organizationUserId: string; + providerUserId: string; + providerOrganizationId: string; + actingUserId: string; + date: string; + deviceType: DeviceType; + ipAddress: string; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.userId = this.getResponseProperty("UserId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.providerId = this.getResponseProperty("ProviderId"); + this.cipherId = this.getResponseProperty("CipherId"); + this.collectionId = this.getResponseProperty("CollectionId"); + this.groupId = this.getResponseProperty("GroupId"); + this.policyId = this.getResponseProperty("PolicyId"); + this.organizationUserId = this.getResponseProperty("OrganizationUserId"); + this.providerUserId = this.getResponseProperty("ProviderUserId"); + this.providerOrganizationId = this.getResponseProperty("ProviderOrganizationId"); + this.actingUserId = this.getResponseProperty("ActingUserId"); + this.date = this.getResponseProperty("Date"); + this.deviceType = this.getResponseProperty("DeviceType"); + this.ipAddress = this.getResponseProperty("IpAddress"); + } +} diff --git a/jslib/common/src/models/response/folderResponse.ts b/jslib/common/src/models/response/folderResponse.ts new file mode 100644 index 00000000..70d4897f --- /dev/null +++ b/jslib/common/src/models/response/folderResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class FolderResponse extends BaseResponse { + id: string; + name: string; + revisionDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + } +} diff --git a/jslib/common/src/models/response/globalDomainResponse.ts b/jslib/common/src/models/response/globalDomainResponse.ts new file mode 100644 index 00000000..e3197e76 --- /dev/null +++ b/jslib/common/src/models/response/globalDomainResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class GlobalDomainResponse extends BaseResponse { + type: number; + domains: string[]; + excluded: boolean; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.domains = this.getResponseProperty("Domains"); + this.excluded = this.getResponseProperty("Excluded"); + } +} diff --git a/jslib/common/src/models/response/groupResponse.ts b/jslib/common/src/models/response/groupResponse.ts new file mode 100644 index 00000000..ab4b58b7 --- /dev/null +++ b/jslib/common/src/models/response/groupResponse.ts @@ -0,0 +1,31 @@ +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class GroupResponse extends BaseResponse { + id: string; + organizationId: string; + name: string; + accessAll: boolean; + externalId: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.name = this.getResponseProperty("Name"); + this.accessAll = this.getResponseProperty("AccessAll"); + this.externalId = this.getResponseProperty("ExternalId"); + } +} + +export class GroupDetailsResponse extends GroupResponse { + collections: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c)); + } + } +} diff --git a/jslib/common/src/models/response/identityCaptchaResponse.ts b/jslib/common/src/models/response/identityCaptchaResponse.ts new file mode 100644 index 00000000..2537057e --- /dev/null +++ b/jslib/common/src/models/response/identityCaptchaResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class IdentityCaptchaResponse extends BaseResponse { + siteKey: string; + + constructor(response: any) { + super(response); + this.siteKey = this.getResponseProperty("HCaptcha_SiteKey"); + } +} diff --git a/jslib/common/src/models/response/identityTokenResponse.ts b/jslib/common/src/models/response/identityTokenResponse.ts new file mode 100644 index 00000000..87b5d396 --- /dev/null +++ b/jslib/common/src/models/response/identityTokenResponse.ts @@ -0,0 +1,38 @@ +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; + +export class IdentityTokenResponse extends BaseResponse { + accessToken: string; + expiresIn: number; + refreshToken: string; + tokenType: string; + + resetMasterPassword: boolean; + privateKey: string; + key: string; + twoFactorToken: string; + kdf: KdfType; + kdfIterations: number; + forcePasswordReset: boolean; + apiUseKeyConnector: boolean; + keyConnectorUrl: string; + + constructor(response: any) { + super(response); + this.accessToken = response.access_token; + this.expiresIn = response.expires_in; + this.refreshToken = response.refresh_token; + this.tokenType = response.token_type; + + this.resetMasterPassword = this.getResponseProperty("ResetMasterPassword"); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.key = this.getResponseProperty("Key"); + this.twoFactorToken = this.getResponseProperty("TwoFactorToken"); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset"); + this.apiUseKeyConnector = this.getResponseProperty("ApiUseKeyConnector"); + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + } +} diff --git a/jslib/common/src/models/response/identityTwoFactorResponse.ts b/jslib/common/src/models/response/identityTwoFactorResponse.ts new file mode 100644 index 00000000..dc7dfde7 --- /dev/null +++ b/jslib/common/src/models/response/identityTwoFactorResponse.ts @@ -0,0 +1,24 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { BaseResponse } from "./baseResponse"; + +export class IdentityTwoFactorResponse extends BaseResponse { + twoFactorProviders: TwoFactorProviderType[]; + twoFactorProviders2 = new Map(); + captchaToken: string; + + constructor(response: any) { + super(response); + this.captchaToken = this.getResponseProperty("CaptchaBypassToken"); + this.twoFactorProviders = this.getResponseProperty("TwoFactorProviders"); + const twoFactorProviders2 = this.getResponseProperty("TwoFactorProviders2"); + if (twoFactorProviders2 != null) { + for (const prop in twoFactorProviders2) { + // eslint-disable-next-line + if (twoFactorProviders2.hasOwnProperty(prop)) { + this.twoFactorProviders2.set(parseInt(prop, null), twoFactorProviders2[prop]); + } + } + } + } +} diff --git a/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts b/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts new file mode 100644 index 00000000..5183397b --- /dev/null +++ b/jslib/common/src/models/response/keyConnectorUserKeyResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class KeyConnectorUserKeyResponse extends BaseResponse { + key: string; + + constructor(response: any) { + super(response); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/keysResponse.ts b/jslib/common/src/models/response/keysResponse.ts new file mode 100644 index 00000000..e1e8e5db --- /dev/null +++ b/jslib/common/src/models/response/keysResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class KeysResponse extends BaseResponse { + privateKey: string; + publicKey: string; + + constructor(response: any) { + super(response); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.publicKey = this.getResponseProperty("PublicKey"); + } +} diff --git a/jslib/common/src/models/response/listResponse.ts b/jslib/common/src/models/response/listResponse.ts new file mode 100644 index 00000000..e37c6697 --- /dev/null +++ b/jslib/common/src/models/response/listResponse.ts @@ -0,0 +1,13 @@ +import { BaseResponse } from "./baseResponse"; + +export class ListResponse extends BaseResponse { + data: T[]; + continuationToken: string; + + constructor(response: any, t: new (dataResponse: any) => T) { + super(response); + const data = this.getResponseProperty("Data"); + this.data = data == null ? [] : data.map((dr: any) => new t(dr)); + this.continuationToken = this.getResponseProperty("ContinuationToken"); + } +} diff --git a/jslib/common/src/models/response/notificationResponse.ts b/jslib/common/src/models/response/notificationResponse.ts new file mode 100644 index 00000000..f23de8fe --- /dev/null +++ b/jslib/common/src/models/response/notificationResponse.ts @@ -0,0 +1,98 @@ +import { NotificationType } from "../../enums/notificationType"; + +import { BaseResponse } from "./baseResponse"; + +export class NotificationResponse extends BaseResponse { + contextId: string; + type: NotificationType; + payload: any; + + constructor(response: any) { + super(response); + this.contextId = this.getResponseProperty("ContextId"); + this.type = this.getResponseProperty("Type"); + + const payload = this.getResponseProperty("Payload"); + switch (this.type) { + case NotificationType.SyncCipherCreate: + case NotificationType.SyncCipherDelete: + case NotificationType.SyncCipherUpdate: + case NotificationType.SyncLoginDelete: + this.payload = new SyncCipherNotification(payload); + break; + case NotificationType.SyncFolderCreate: + case NotificationType.SyncFolderDelete: + case NotificationType.SyncFolderUpdate: + this.payload = new SyncFolderNotification(payload); + break; + case NotificationType.SyncVault: + case NotificationType.SyncCiphers: + case NotificationType.SyncOrgKeys: + case NotificationType.SyncSettings: + case NotificationType.LogOut: + this.payload = new UserNotification(payload); + break; + case NotificationType.SyncSendCreate: + case NotificationType.SyncSendUpdate: + case NotificationType.SyncSendDelete: + this.payload = new SyncSendNotification(payload); + break; + default: + break; + } + } +} + +export class SyncCipherNotification extends BaseResponse { + id: string; + userId: string; + organizationId: string; + collectionIds: string[]; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.collectionIds = this.getResponseProperty("CollectionIds"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} + +export class SyncFolderNotification extends BaseResponse { + id: string; + userId: string; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} + +export class UserNotification extends BaseResponse { + userId: string; + date: Date; + + constructor(response: any) { + super(response); + this.userId = this.getResponseProperty("UserId"); + this.date = new Date(this.getResponseProperty("Date")); + } +} + +export class SyncSendNotification extends BaseResponse { + id: string; + userId: string; + revisionDate: Date; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); + } +} diff --git a/jslib/common/src/models/response/organization/organizationSsoResponse.ts b/jslib/common/src/models/response/organization/organizationSsoResponse.ts new file mode 100644 index 00000000..abe6b720 --- /dev/null +++ b/jslib/common/src/models/response/organization/organizationSsoResponse.ts @@ -0,0 +1,35 @@ +import { SsoConfigApi } from "../../api/ssoConfigApi"; +import { BaseResponse } from "../baseResponse"; + +export class OrganizationSsoResponse extends BaseResponse { + enabled: boolean; + data: SsoConfigApi; + urls: SsoUrls; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.data = + this.getResponseProperty("Data") != null + ? new SsoConfigApi(this.getResponseProperty("Data")) + : null; + this.urls = new SsoUrls(this.getResponseProperty("Urls")); + } +} + +class SsoUrls extends BaseResponse { + callbackPath: string; + signedOutCallbackPath: string; + spEntityId: string; + spMetadataUrl: string; + spAcsUrl: string; + + constructor(response: any) { + super(response); + this.callbackPath = this.getResponseProperty("CallbackPath"); + this.signedOutCallbackPath = this.getResponseProperty("SignedOutCallbackPath"); + this.spEntityId = this.getResponseProperty("SpEntityId"); + this.spMetadataUrl = this.getResponseProperty("SpMetadataUrl"); + this.spAcsUrl = this.getResponseProperty("SpAcsUrl"); + } +} diff --git a/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts b/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts new file mode 100644 index 00000000..d960463e --- /dev/null +++ b/jslib/common/src/models/response/organizationAutoEnrollStatusResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationAutoEnrollStatusResponse extends BaseResponse { + id: string; + resetPasswordEnabled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.resetPasswordEnabled = this.getResponseProperty("ResetPasswordEnabled"); + } +} diff --git a/jslib/common/src/models/response/organizationKeysResponse.ts b/jslib/common/src/models/response/organizationKeysResponse.ts new file mode 100644 index 00000000..3d5c0d29 --- /dev/null +++ b/jslib/common/src/models/response/organizationKeysResponse.ts @@ -0,0 +1,7 @@ +import { KeysResponse } from "./keysResponse"; + +export class OrganizationKeysResponse extends KeysResponse { + constructor(response: any) { + super(response); + } +} diff --git a/jslib/common/src/models/response/organizationResponse.ts b/jslib/common/src/models/response/organizationResponse.ts new file mode 100644 index 00000000..addb2a28 --- /dev/null +++ b/jslib/common/src/models/response/organizationResponse.ts @@ -0,0 +1,60 @@ +import { PlanType } from "../../enums/planType"; + +import { BaseResponse } from "./baseResponse"; +import { PlanResponse } from "./planResponse"; + +export class OrganizationResponse extends BaseResponse { + id: string; + identifier: string; + name: string; + businessName: string; + businessAddress1: string; + businessAddress2: string; + businessAddress3: string; + businessCountry: string; + businessTaxNumber: string; + billingEmail: string; + plan: PlanResponse; + planType: PlanType; + seats: number; + maxAutoscaleSeats: number; + maxCollections: number; + maxStorageGb: number; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useResetPassword: boolean; + hasPublicAndPrivateKeys: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.identifier = this.getResponseProperty("Identifier"); + this.name = this.getResponseProperty("Name"); + this.businessName = this.getResponseProperty("BusinessName"); + this.businessAddress1 = this.getResponseProperty("BusinessAddress1"); + this.businessAddress2 = this.getResponseProperty("BusinessAddress2"); + this.businessAddress3 = this.getResponseProperty("BusinessAddress3"); + this.businessCountry = this.getResponseProperty("BusinessCountry"); + this.businessTaxNumber = this.getResponseProperty("BusinessTaxNumber"); + this.billingEmail = this.getResponseProperty("BillingEmail"); + const plan = this.getResponseProperty("Plan"); + this.plan = plan == null ? null : new PlanResponse(plan); + this.planType = this.getResponseProperty("PlanType"); + this.seats = this.getResponseProperty("Seats"); + this.maxAutoscaleSeats = this.getResponseProperty("MaxAutoscaleSeats"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.useGroups = this.getResponseProperty("UseGroups"); + this.useDirectory = this.getResponseProperty("UseDirectory"); + this.useEvents = this.getResponseProperty("UseEvents"); + this.useTotp = this.getResponseProperty("UseTotp"); + this.use2fa = this.getResponseProperty("Use2fa"); + this.useApi = this.getResponseProperty("UseApi"); + this.useResetPassword = this.getResponseProperty("UseResetPassword"); + this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys"); + } +} diff --git a/jslib/common/src/models/response/organizationSubscriptionResponse.ts b/jslib/common/src/models/response/organizationSubscriptionResponse.ts new file mode 100644 index 00000000..1985dc6e --- /dev/null +++ b/jslib/common/src/models/response/organizationSubscriptionResponse.ts @@ -0,0 +1,27 @@ +import { OrganizationResponse } from "./organizationResponse"; +import { + BillingSubscriptionResponse, + BillingSubscriptionUpcomingInvoiceResponse, +} from "./subscriptionResponse"; + +export class OrganizationSubscriptionResponse extends OrganizationResponse { + storageName: string; + storageGb: number; + subscription: BillingSubscriptionResponse; + upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse; + expiration: string; + + constructor(response: any) { + super(response); + this.storageName = this.getResponseProperty("StorageName"); + this.storageGb = this.getResponseProperty("StorageGb"); + const subscription = this.getResponseProperty("Subscription"); + this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription); + const upcomingInvoice = this.getResponseProperty("UpcomingInvoice"); + this.upcomingInvoice = + upcomingInvoice == null + ? null + : new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice); + this.expiration = this.getResponseProperty("Expiration"); + } +} diff --git a/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts b/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts new file mode 100644 index 00000000..1b77e036 --- /dev/null +++ b/jslib/common/src/models/response/organizationUserBulkPublicKeyResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationUserBulkPublicKeyResponse extends BaseResponse { + id: string; + userId: string; + key: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/organizationUserBulkResponse.ts b/jslib/common/src/models/response/organizationUserBulkResponse.ts new file mode 100644 index 00000000..15c6f371 --- /dev/null +++ b/jslib/common/src/models/response/organizationUserBulkResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class OrganizationUserBulkResponse extends BaseResponse { + id: string; + error: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.error = this.getResponseProperty("Error"); + } +} diff --git a/jslib/common/src/models/response/organizationUserResponse.ts b/jslib/common/src/models/response/organizationUserResponse.ts new file mode 100644 index 00000000..3e49375b --- /dev/null +++ b/jslib/common/src/models/response/organizationUserResponse.ts @@ -0,0 +1,70 @@ +import { KdfType } from "../../enums/kdfType"; +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; +import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse"; + +export class OrganizationUserResponse extends BaseResponse { + id: string; + userId: string; + type: OrganizationUserType; + status: OrganizationUserStatusType; + accessAll: boolean; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.permissions = new PermissionsApi(this.getResponseProperty("Permissions")); + this.accessAll = this.getResponseProperty("AccessAll"); + this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled"); + } +} + +export class OrganizationUserUserDetailsResponse extends OrganizationUserResponse { + name: string; + email: string; + twoFactorEnabled: boolean; + usesKeyConnector: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled"); + this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + } +} + +export class OrganizationUserDetailsResponse extends OrganizationUserResponse { + collections: SelectionReadOnlyResponse[] = []; + + constructor(response: any) { + super(response); + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c)); + } + } +} + +export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse { + kdf: KdfType; + kdfIterations: number; + resetPasswordKey: string; + encryptedPrivateKey: string; + + constructor(response: any) { + super(response); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.resetPasswordKey = this.getResponseProperty("ResetPasswordKey"); + this.encryptedPrivateKey = this.getResponseProperty("EncryptedPrivateKey"); + } +} diff --git a/jslib/common/src/models/response/passwordHistoryResponse.ts b/jslib/common/src/models/response/passwordHistoryResponse.ts new file mode 100644 index 00000000..f3a8a133 --- /dev/null +++ b/jslib/common/src/models/response/passwordHistoryResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class PasswordHistoryResponse extends BaseResponse { + password: string; + lastUsedDate: string; + + constructor(response: any) { + super(response); + this.password = this.getResponseProperty("Password"); + this.lastUsedDate = this.getResponseProperty("LastUsedDate"); + } +} diff --git a/jslib/common/src/models/response/paymentResponse.ts b/jslib/common/src/models/response/paymentResponse.ts new file mode 100644 index 00000000..ec4977cd --- /dev/null +++ b/jslib/common/src/models/response/paymentResponse.ts @@ -0,0 +1,18 @@ +import { BaseResponse } from "./baseResponse"; +import { ProfileResponse } from "./profileResponse"; + +export class PaymentResponse extends BaseResponse { + userProfile: ProfileResponse; + paymentIntentClientSecret: string; + success: boolean; + + constructor(response: any) { + super(response); + const userProfile = this.getResponseProperty("UserProfile"); + if (userProfile != null) { + this.userProfile = new ProfileResponse(userProfile); + } + this.paymentIntentClientSecret = this.getResponseProperty("PaymentIntentClientSecret"); + this.success = this.getResponseProperty("Success"); + } +} diff --git a/jslib/common/src/models/response/planResponse.ts b/jslib/common/src/models/response/planResponse.ts new file mode 100644 index 00000000..04652542 --- /dev/null +++ b/jslib/common/src/models/response/planResponse.ts @@ -0,0 +1,95 @@ +import { PlanType } from "../../enums/planType"; +import { ProductType } from "../../enums/productType"; + +import { BaseResponse } from "./baseResponse"; + +export class PlanResponse extends BaseResponse { + type: PlanType; + product: ProductType; + name: string; + isAnnual: boolean; + nameLocalizationKey: string; + descriptionLocalizationKey: string; + canBeUsedByBusiness: boolean; + baseSeats: number; + baseStorageGb: number; + maxCollections: number; + maxUsers: number; + + hasAdditionalSeatsOption: boolean; + maxAdditionalSeats: number; + hasAdditionalStorageOption: boolean; + maxAdditionalStorage: number; + hasPremiumAccessOption: boolean; + trialPeriodDays: number; + + hasSelfHost: boolean; + hasPolicies: boolean; + hasGroups: boolean; + hasDirectory: boolean; + hasEvents: boolean; + hasTotp: boolean; + has2fa: boolean; + hasApi: boolean; + hasSso: boolean; + hasResetPassword: boolean; + usersGetPremium: boolean; + + upgradeSortOrder: number; + displaySortOrder: number; + legacyYear: number; + disabled: boolean; + + stripePlanId: string; + stripeSeatPlanId: string; + stripeStoragePlanId: string; + stripePremiumAccessPlanId: string; + basePrice: number; + seatPrice: number; + additionalStoragePricePerGb: number; + premiumAccessOptionPrice: number; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + this.product = this.getResponseProperty("Product"); + this.name = this.getResponseProperty("Name"); + this.isAnnual = this.getResponseProperty("IsAnnual"); + this.nameLocalizationKey = this.getResponseProperty("NameLocalizationKey"); + this.descriptionLocalizationKey = this.getResponseProperty("DescriptionLocalizationKey"); + this.canBeUsedByBusiness = this.getResponseProperty("CanBeUsedByBusiness"); + this.baseSeats = this.getResponseProperty("BaseSeats"); + this.baseStorageGb = this.getResponseProperty("BaseStorageGb"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxUsers = this.getResponseProperty("MaxUsers"); + this.hasAdditionalSeatsOption = this.getResponseProperty("HasAdditionalSeatsOption"); + this.maxAdditionalSeats = this.getResponseProperty("MaxAdditionalSeats"); + this.hasAdditionalStorageOption = this.getResponseProperty("HasAdditionalStorageOption"); + this.maxAdditionalStorage = this.getResponseProperty("MaxAdditionalStorage"); + this.hasPremiumAccessOption = this.getResponseProperty("HasPremiumAccessOption"); + this.trialPeriodDays = this.getResponseProperty("TrialPeriodDays"); + this.hasSelfHost = this.getResponseProperty("HasSelfHost"); + this.hasPolicies = this.getResponseProperty("HasPolicies"); + this.hasGroups = this.getResponseProperty("HasGroups"); + this.hasDirectory = this.getResponseProperty("HasDirectory"); + this.hasEvents = this.getResponseProperty("HasEvents"); + this.hasTotp = this.getResponseProperty("HasTotp"); + this.has2fa = this.getResponseProperty("Has2fa"); + this.hasApi = this.getResponseProperty("HasApi"); + this.hasSso = this.getResponseProperty("HasSso"); + this.hasResetPassword = this.getResponseProperty("HasResetPassword"); + this.usersGetPremium = this.getResponseProperty("UsersGetPremium"); + this.upgradeSortOrder = this.getResponseProperty("UpgradeSortOrder"); + this.displaySortOrder = this.getResponseProperty("SortOrder"); + this.legacyYear = this.getResponseProperty("LegacyYear"); + this.disabled = this.getResponseProperty("Disabled"); + this.stripePlanId = this.getResponseProperty("StripePlanId"); + this.stripeSeatPlanId = this.getResponseProperty("StripeSeatPlanId"); + this.stripeStoragePlanId = this.getResponseProperty("StripeStoragePlanId"); + this.stripePremiumAccessPlanId = this.getResponseProperty("StripePremiumAccessPlanId"); + this.basePrice = this.getResponseProperty("BasePrice"); + this.seatPrice = this.getResponseProperty("SeatPrice"); + this.additionalStoragePricePerGb = this.getResponseProperty("AdditionalStoragePricePerGb"); + this.premiumAccessOptionPrice = this.getResponseProperty("PremiumAccessOptionPrice"); + } +} diff --git a/jslib/common/src/models/response/policyResponse.ts b/jslib/common/src/models/response/policyResponse.ts new file mode 100644 index 00000000..d38375ef --- /dev/null +++ b/jslib/common/src/models/response/policyResponse.ts @@ -0,0 +1,20 @@ +import { PolicyType } from "../../enums/policyType"; + +import { BaseResponse } from "./baseResponse"; + +export class PolicyResponse extends BaseResponse { + id: string; + organizationId: string; + type: PolicyType; + data: any; + enabled: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.type = this.getResponseProperty("Type"); + this.data = this.getResponseProperty("Data"); + this.enabled = this.getResponseProperty("Enabled"); + } +} diff --git a/jslib/common/src/models/response/preloginResponse.ts b/jslib/common/src/models/response/preloginResponse.ts new file mode 100644 index 00000000..7756f8ed --- /dev/null +++ b/jslib/common/src/models/response/preloginResponse.ts @@ -0,0 +1,14 @@ +import { KdfType } from "../../enums/kdfType"; + +import { BaseResponse } from "./baseResponse"; + +export class PreloginResponse extends BaseResponse { + kdf: KdfType; + kdfIterations: number; + + constructor(response: any) { + super(response); + this.kdf = this.getResponseProperty("Kdf"); + this.kdfIterations = this.getResponseProperty("KdfIterations"); + } +} diff --git a/jslib/common/src/models/response/profileOrganizationResponse.ts b/jslib/common/src/models/response/profileOrganizationResponse.ts new file mode 100644 index 00000000..9c992e54 --- /dev/null +++ b/jslib/common/src/models/response/profileOrganizationResponse.ts @@ -0,0 +1,81 @@ +import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../../enums/organizationUserType"; +import { ProductType } from "../../enums/productType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; + +export class ProfileOrganizationResponse extends BaseResponse { + id: string; + name: string; + usePolicies: boolean; + useGroups: boolean; + useDirectory: boolean; + useEvents: boolean; + useTotp: boolean; + use2fa: boolean; + useApi: boolean; + useSso: boolean; + useKeyConnector: boolean; + useResetPassword: boolean; + selfHost: boolean; + usersGetPremium: boolean; + seats: number; + maxCollections: number; + maxStorageGb?: number; + key: string; + hasPublicAndPrivateKeys: boolean; + status: OrganizationUserStatusType; + type: OrganizationUserType; + enabled: boolean; + ssoBound: boolean; + identifier: string; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + userId: string; + providerId: string; + providerName: string; + familySponsorshipFriendlyName: string; + familySponsorshipAvailable: boolean; + planProductType: ProductType; + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.usePolicies = this.getResponseProperty("UsePolicies"); + this.useGroups = this.getResponseProperty("UseGroups"); + this.useDirectory = this.getResponseProperty("UseDirectory"); + this.useEvents = this.getResponseProperty("UseEvents"); + this.useTotp = this.getResponseProperty("UseTotp"); + this.use2fa = this.getResponseProperty("Use2fa"); + this.useApi = this.getResponseProperty("UseApi"); + this.useSso = this.getResponseProperty("UseSso"); + this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false; + this.useResetPassword = this.getResponseProperty("UseResetPassword"); + this.selfHost = this.getResponseProperty("SelfHost"); + this.usersGetPremium = this.getResponseProperty("UsersGetPremium"); + this.seats = this.getResponseProperty("Seats"); + this.maxCollections = this.getResponseProperty("MaxCollections"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.key = this.getResponseProperty("Key"); + this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys"); + this.status = this.getResponseProperty("Status"); + this.type = this.getResponseProperty("Type"); + this.enabled = this.getResponseProperty("Enabled"); + this.ssoBound = this.getResponseProperty("SsoBound"); + this.identifier = this.getResponseProperty("Identifier"); + this.permissions = new PermissionsApi(this.getResponseProperty("permissions")); + this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled"); + this.userId = this.getResponseProperty("UserId"); + this.providerId = this.getResponseProperty("ProviderId"); + this.providerName = this.getResponseProperty("ProviderName"); + this.familySponsorshipFriendlyName = this.getResponseProperty("FamilySponsorshipFriendlyName"); + this.familySponsorshipAvailable = this.getResponseProperty("FamilySponsorshipAvailable"); + this.planProductType = this.getResponseProperty("PlanProductType"); + this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled") ?? false; + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + } +} diff --git a/jslib/common/src/models/response/profileProviderOrganizationResponse.ts b/jslib/common/src/models/response/profileProviderOrganizationResponse.ts new file mode 100644 index 00000000..74b74c00 --- /dev/null +++ b/jslib/common/src/models/response/profileProviderOrganizationResponse.ts @@ -0,0 +1,8 @@ +import { ProfileOrganizationResponse } from "./profileOrganizationResponse"; + +export class ProfileProviderOrganizationResponse extends ProfileOrganizationResponse { + constructor(response: any) { + super(response); + this.keyConnectorEnabled = false; + } +} diff --git a/jslib/common/src/models/response/profileProviderResponse.ts b/jslib/common/src/models/response/profileProviderResponse.ts new file mode 100644 index 00000000..84e8bbf6 --- /dev/null +++ b/jslib/common/src/models/response/profileProviderResponse.ts @@ -0,0 +1,30 @@ +import { ProviderUserStatusType } from "../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../enums/providerUserType"; +import { PermissionsApi } from "../api/permissionsApi"; + +import { BaseResponse } from "./baseResponse"; + +export class ProfileProviderResponse extends BaseResponse { + id: string; + name: string; + key: string; + status: ProviderUserStatusType; + type: ProviderUserType; + enabled: boolean; + permissions: PermissionsApi; + userId: string; + useEvents: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.key = this.getResponseProperty("Key"); + this.status = this.getResponseProperty("Status"); + this.type = this.getResponseProperty("Type"); + this.enabled = this.getResponseProperty("Enabled"); + this.permissions = new PermissionsApi(this.getResponseProperty("permissions")); + this.userId = this.getResponseProperty("UserId"); + this.useEvents = this.getResponseProperty("UseEvents"); + } +} diff --git a/jslib/common/src/models/response/profileResponse.ts b/jslib/common/src/models/response/profileResponse.ts new file mode 100644 index 00000000..2c3e66fb --- /dev/null +++ b/jslib/common/src/models/response/profileResponse.ts @@ -0,0 +1,55 @@ +import { BaseResponse } from "./baseResponse"; +import { ProfileOrganizationResponse } from "./profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "./profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "./profileProviderResponse"; + +export class ProfileResponse extends BaseResponse { + id: string; + name: string; + email: string; + emailVerified: boolean; + masterPasswordHint: string; + premium: boolean; + culture: string; + twoFactorEnabled: boolean; + key: string; + privateKey: string; + securityStamp: string; + forcePasswordReset: boolean; + usesKeyConnector: boolean; + organizations: ProfileOrganizationResponse[] = []; + providers: ProfileProviderResponse[] = []; + providerOrganizations: ProfileProviderOrganizationResponse[] = []; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + this.emailVerified = this.getResponseProperty("EmailVerified"); + this.masterPasswordHint = this.getResponseProperty("MasterPasswordHint"); + this.premium = this.getResponseProperty("Premium"); + this.culture = this.getResponseProperty("Culture"); + this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled"); + this.key = this.getResponseProperty("Key"); + this.privateKey = this.getResponseProperty("PrivateKey"); + this.securityStamp = this.getResponseProperty("SecurityStamp"); + this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false; + this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + + const organizations = this.getResponseProperty("Organizations"); + if (organizations != null) { + this.organizations = organizations.map((o: any) => new ProfileOrganizationResponse(o)); + } + const providers = this.getResponseProperty("Providers"); + if (providers != null) { + this.providers = providers.map((o: any) => new ProfileProviderResponse(o)); + } + const providerOrganizations = this.getResponseProperty("ProviderOrganizations"); + if (providerOrganizations != null) { + this.providerOrganizations = providerOrganizations.map( + (o: any) => new ProfileProviderOrganizationResponse(o) + ); + } + } +} diff --git a/jslib/common/src/models/response/provider/providerOrganizationResponse.ts b/jslib/common/src/models/response/provider/providerOrganizationResponse.ts new file mode 100644 index 00000000..d733362a --- /dev/null +++ b/jslib/common/src/models/response/provider/providerOrganizationResponse.ts @@ -0,0 +1,31 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderOrganizationResponse extends BaseResponse { + id: string; + providerId: string; + organizationId: string; + key: string; + settings: string; + creationDate: string; + revisionDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.providerId = this.getResponseProperty("ProviderId"); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.key = this.getResponseProperty("Key"); + this.settings = this.getResponseProperty("Settings"); + this.creationDate = this.getResponseProperty("CreationDate"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + } +} + +export class ProviderOrganizationOrganizationDetailsResponse extends ProviderOrganizationResponse { + organizationName: string; + + constructor(response: any) { + super(response); + this.organizationName = this.getResponseProperty("OrganizationName"); + } +} diff --git a/jslib/common/src/models/response/provider/providerResponse.ts b/jslib/common/src/models/response/provider/providerResponse.ts new file mode 100644 index 00000000..2b8729fa --- /dev/null +++ b/jslib/common/src/models/response/provider/providerResponse.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderResponse extends BaseResponse { + id: string; + name: string; + businessName: string; + billingEmail: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.name = this.getResponseProperty("Name"); + this.businessName = this.getResponseProperty("BusinessName"); + this.billingEmail = this.getResponseProperty("BillingEmail"); + } +} diff --git a/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts b/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts new file mode 100644 index 00000000..ad078f98 --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserBulkPublicKeyResponse.ts @@ -0,0 +1,3 @@ +import { OrganizationUserBulkPublicKeyResponse } from "../organizationUserBulkPublicKeyResponse"; + +export class ProviderUserBulkPublicKeyResponse extends OrganizationUserBulkPublicKeyResponse {} diff --git a/jslib/common/src/models/response/provider/providerUserBulkResponse.ts b/jslib/common/src/models/response/provider/providerUserBulkResponse.ts new file mode 100644 index 00000000..6cf12f96 --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserBulkResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "../baseResponse"; + +export class ProviderUserBulkResponse extends BaseResponse { + id: string; + error: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.error = this.getResponseProperty("Error"); + } +} diff --git a/jslib/common/src/models/response/provider/providerUserResponse.ts b/jslib/common/src/models/response/provider/providerUserResponse.ts new file mode 100644 index 00000000..cf1a181c --- /dev/null +++ b/jslib/common/src/models/response/provider/providerUserResponse.ts @@ -0,0 +1,32 @@ +import { ProviderUserStatusType } from "../../../enums/providerUserStatusType"; +import { ProviderUserType } from "../../../enums/providerUserType"; +import { PermissionsApi } from "../../api/permissionsApi"; +import { BaseResponse } from "../baseResponse"; + +export class ProviderUserResponse extends BaseResponse { + id: string; + userId: string; + type: ProviderUserType; + status: ProviderUserStatusType; + permissions: PermissionsApi; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + this.type = this.getResponseProperty("Type"); + this.status = this.getResponseProperty("Status"); + this.permissions = new PermissionsApi(this.getResponseProperty("Permissions")); + } +} + +export class ProviderUserUserDetailsResponse extends ProviderUserResponse { + name: string; + email: string; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.email = this.getResponseProperty("Email"); + } +} diff --git a/jslib/common/src/models/response/selectionReadOnlyResponse.ts b/jslib/common/src/models/response/selectionReadOnlyResponse.ts new file mode 100644 index 00000000..cee9361c --- /dev/null +++ b/jslib/common/src/models/response/selectionReadOnlyResponse.ts @@ -0,0 +1,14 @@ +import { BaseResponse } from "./baseResponse"; + +export class SelectionReadOnlyResponse extends BaseResponse { + id: string; + readOnly: boolean; + hidePasswords: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.readOnly = this.getResponseProperty("ReadOnly"); + this.hidePasswords = this.getResponseProperty("HidePasswords"); + } +} diff --git a/jslib/common/src/models/response/sendAccessResponse.ts b/jslib/common/src/models/response/sendAccessResponse.ts new file mode 100644 index 00000000..e98a9d05 --- /dev/null +++ b/jslib/common/src/models/response/sendAccessResponse.ts @@ -0,0 +1,35 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; + +import { BaseResponse } from "./baseResponse"; + +export class SendAccessResponse extends BaseResponse { + id: string; + type: SendType; + name: string; + file: SendFileApi; + text: SendTextApi; + expirationDate: Date; + creatorIdentifier: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + + const text = this.getResponseProperty("Text"); + if (text != null) { + this.text = new SendTextApi(text); + } + + const file = this.getResponseProperty("File"); + if (file != null) { + this.file = new SendFileApi(file); + } + + this.expirationDate = this.getResponseProperty("ExpirationDate"); + this.creatorIdentifier = this.getResponseProperty("CreatorIdentifier"); + } +} diff --git a/jslib/common/src/models/response/sendFileDownloadDataResponse.ts b/jslib/common/src/models/response/sendFileDownloadDataResponse.ts new file mode 100644 index 00000000..ca2575a2 --- /dev/null +++ b/jslib/common/src/models/response/sendFileDownloadDataResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class SendFileDownloadDataResponse extends BaseResponse { + id: string = null; + url: string = null; + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/sendFileUploadDataResponse.ts b/jslib/common/src/models/response/sendFileUploadDataResponse.ts new file mode 100644 index 00000000..b787f2cc --- /dev/null +++ b/jslib/common/src/models/response/sendFileUploadDataResponse.ts @@ -0,0 +1,17 @@ +import { FileUploadType } from "../../enums/fileUploadType"; + +import { BaseResponse } from "./baseResponse"; +import { SendResponse } from "./sendResponse"; + +export class SendFileUploadDataResponse extends BaseResponse { + fileUploadType: FileUploadType; + sendResponse: SendResponse; + url: string = null; + constructor(response: any) { + super(response); + this.fileUploadType = this.getResponseProperty("FileUploadType"); + const sendResponse = this.getResponseProperty("SendResponse"); + this.sendResponse = sendResponse == null ? null : new SendResponse(sendResponse); + this.url = this.getResponseProperty("Url"); + } +} diff --git a/jslib/common/src/models/response/sendResponse.ts b/jslib/common/src/models/response/sendResponse.ts new file mode 100644 index 00000000..28750725 --- /dev/null +++ b/jslib/common/src/models/response/sendResponse.ts @@ -0,0 +1,52 @@ +import { SendType } from "../../enums/sendType"; +import { SendFileApi } from "../api/sendFileApi"; +import { SendTextApi } from "../api/sendTextApi"; + +import { BaseResponse } from "./baseResponse"; + +export class SendResponse extends BaseResponse { + id: string; + accessId: string; + type: SendType; + name: string; + notes: string; + file: SendFileApi; + text: SendTextApi; + key: string; + maxAccessCount?: number; + accessCount: number; + revisionDate: string; + expirationDate: string; + deletionDate: string; + password: string; + disable: boolean; + hideEmail: boolean; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.accessId = this.getResponseProperty("AccessId"); + this.type = this.getResponseProperty("Type"); + this.name = this.getResponseProperty("Name"); + this.notes = this.getResponseProperty("Notes"); + this.key = this.getResponseProperty("Key"); + this.maxAccessCount = this.getResponseProperty("MaxAccessCount"); + this.accessCount = this.getResponseProperty("AccessCount"); + this.revisionDate = this.getResponseProperty("RevisionDate"); + this.expirationDate = this.getResponseProperty("ExpirationDate"); + this.deletionDate = this.getResponseProperty("DeletionDate"); + this.password = this.getResponseProperty("Password"); + this.disable = this.getResponseProperty("Disabled") || false; + this.hideEmail = this.getResponseProperty("HideEmail") || false; + + const text = this.getResponseProperty("Text"); + if (text != null) { + this.text = new SendTextApi(text); + } + + const file = this.getResponseProperty("File"); + if (file != null) { + this.file = new SendFileApi(file); + } + } +} diff --git a/jslib/common/src/models/response/subscriptionResponse.ts b/jslib/common/src/models/response/subscriptionResponse.ts new file mode 100644 index 00000000..fc5570c3 --- /dev/null +++ b/jslib/common/src/models/response/subscriptionResponse.ts @@ -0,0 +1,85 @@ +import { BaseResponse } from "./baseResponse"; + +export class SubscriptionResponse extends BaseResponse { + storageName: string; + storageGb: number; + maxStorageGb: number; + subscription: BillingSubscriptionResponse; + upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse; + license: any; + expiration: string; + usingInAppPurchase: boolean; + + constructor(response: any) { + super(response); + this.storageName = this.getResponseProperty("StorageName"); + this.storageGb = this.getResponseProperty("StorageGb"); + this.maxStorageGb = this.getResponseProperty("MaxStorageGb"); + this.license = this.getResponseProperty("License"); + this.expiration = this.getResponseProperty("Expiration"); + this.usingInAppPurchase = this.getResponseProperty("UsingInAppPurchase"); + const subscription = this.getResponseProperty("Subscription"); + const upcomingInvoice = this.getResponseProperty("UpcomingInvoice"); + this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription); + this.upcomingInvoice = + upcomingInvoice == null + ? null + : new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice); + } +} + +export class BillingSubscriptionResponse extends BaseResponse { + trialStartDate: string; + trialEndDate: string; + periodStartDate: string; + periodEndDate: string; + cancelledDate: string; + cancelAtEndDate: boolean; + status: string; + cancelled: boolean; + items: BillingSubscriptionItemResponse[] = []; + + constructor(response: any) { + super(response); + this.trialEndDate = this.getResponseProperty("TrialStartDate"); + this.trialEndDate = this.getResponseProperty("TrialEndDate"); + this.periodStartDate = this.getResponseProperty("PeriodStartDate"); + this.periodEndDate = this.getResponseProperty("PeriodEndDate"); + this.cancelledDate = this.getResponseProperty("CancelledDate"); + this.cancelAtEndDate = this.getResponseProperty("CancelAtEndDate"); + this.status = this.getResponseProperty("Status"); + this.cancelled = this.getResponseProperty("Cancelled"); + const items = this.getResponseProperty("Items"); + if (items != null) { + this.items = items.map((i: any) => new BillingSubscriptionItemResponse(i)); + } + } +} + +export class BillingSubscriptionItemResponse extends BaseResponse { + name: string; + amount: number; + quantity: number; + interval: string; + sponsoredSubscriptionItem: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.amount = this.getResponseProperty("Amount"); + this.quantity = this.getResponseProperty("Quantity"); + this.interval = this.getResponseProperty("Interval"); + this.sponsoredSubscriptionItem = this.getResponseProperty("SponsoredSubscriptionItem"); + } +} + +export class BillingSubscriptionUpcomingInvoiceResponse extends BaseResponse { + date: string; + amount: number; + + constructor(response: any) { + super(response); + this.date = this.getResponseProperty("Date"); + this.amount = this.getResponseProperty("Amount"); + } +} diff --git a/jslib/common/src/models/response/syncResponse.ts b/jslib/common/src/models/response/syncResponse.ts new file mode 100644 index 00000000..cab3ff2e --- /dev/null +++ b/jslib/common/src/models/response/syncResponse.ts @@ -0,0 +1,57 @@ +import { BaseResponse } from "./baseResponse"; +import { CipherResponse } from "./cipherResponse"; +import { CollectionDetailsResponse } from "./collectionResponse"; +import { DomainsResponse } from "./domainsResponse"; +import { FolderResponse } from "./folderResponse"; +import { PolicyResponse } from "./policyResponse"; +import { ProfileResponse } from "./profileResponse"; +import { SendResponse } from "./sendResponse"; + +export class SyncResponse extends BaseResponse { + profile?: ProfileResponse; + folders: FolderResponse[] = []; + collections: CollectionDetailsResponse[] = []; + ciphers: CipherResponse[] = []; + domains?: DomainsResponse; + policies?: PolicyResponse[] = []; + sends: SendResponse[] = []; + + constructor(response: any) { + super(response); + + const profile = this.getResponseProperty("Profile"); + if (profile != null) { + this.profile = new ProfileResponse(profile); + } + + const folders = this.getResponseProperty("Folders"); + if (folders != null) { + this.folders = folders.map((f: any) => new FolderResponse(f)); + } + + const collections = this.getResponseProperty("Collections"); + if (collections != null) { + this.collections = collections.map((c: any) => new CollectionDetailsResponse(c)); + } + + const ciphers = this.getResponseProperty("Ciphers"); + if (ciphers != null) { + this.ciphers = ciphers.map((c: any) => new CipherResponse(c)); + } + + const domains = this.getResponseProperty("Domains"); + if (domains != null) { + this.domains = new DomainsResponse(domains); + } + + const policies = this.getResponseProperty("Policies"); + if (policies != null) { + this.policies = policies.map((p: any) => new PolicyResponse(p)); + } + + const sends = this.getResponseProperty("Sends"); + if (sends != null) { + this.sends = sends.map((s: any) => new SendResponse(s)); + } + } +} diff --git a/jslib/common/src/models/response/taxInfoResponse.ts b/jslib/common/src/models/response/taxInfoResponse.ts new file mode 100644 index 00000000..bb4693ca --- /dev/null +++ b/jslib/common/src/models/response/taxInfoResponse.ts @@ -0,0 +1,24 @@ +import { BaseResponse } from "./baseResponse"; + +export class TaxInfoResponse extends BaseResponse { + taxId: string; + taxIdType: string; + line1: string; + line2: string; + city: string; + state: string; + country: string; + postalCode: string; + + constructor(response: any) { + super(response); + this.taxId = this.getResponseProperty("TaxIdNumber"); + this.taxIdType = this.getResponseProperty("TaxIdType"); + this.line1 = this.getResponseProperty("Line1"); + this.line2 = this.getResponseProperty("Line2"); + this.city = this.getResponseProperty("City"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.country = this.getResponseProperty("Country"); + } +} diff --git a/jslib/common/src/models/response/taxRateResponse.ts b/jslib/common/src/models/response/taxRateResponse.ts new file mode 100644 index 00000000..28274a50 --- /dev/null +++ b/jslib/common/src/models/response/taxRateResponse.ts @@ -0,0 +1,18 @@ +import { BaseResponse } from "./baseResponse"; + +export class TaxRateResponse extends BaseResponse { + id: string; + country: string; + state: string; + postalCode: string; + rate: number; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.country = this.getResponseProperty("Country"); + this.state = this.getResponseProperty("State"); + this.postalCode = this.getResponseProperty("PostalCode"); + this.rate = this.getResponseProperty("Rate"); + } +} diff --git a/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts b/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts new file mode 100644 index 00000000..8eb73eaa --- /dev/null +++ b/jslib/common/src/models/response/twoFactorAuthenticatorResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorAuthenticatorResponse extends BaseResponse { + enabled: boolean; + key: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.key = this.getResponseProperty("Key"); + } +} diff --git a/jslib/common/src/models/response/twoFactorDuoResponse.ts b/jslib/common/src/models/response/twoFactorDuoResponse.ts new file mode 100644 index 00000000..401de796 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorDuoResponse.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorDuoResponse extends BaseResponse { + enabled: boolean; + host: string; + secretKey: string; + integrationKey: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.host = this.getResponseProperty("Host"); + this.secretKey = this.getResponseProperty("SecretKey"); + this.integrationKey = this.getResponseProperty("IntegrationKey"); + } +} diff --git a/jslib/common/src/models/response/twoFactorEmailResponse.ts b/jslib/common/src/models/response/twoFactorEmailResponse.ts new file mode 100644 index 00000000..ad1936d3 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorEmailResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorEmailResponse extends BaseResponse { + enabled: boolean; + email: string; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.email = this.getResponseProperty("Email"); + } +} diff --git a/jslib/common/src/models/response/twoFactorProviderResponse.ts b/jslib/common/src/models/response/twoFactorProviderResponse.ts new file mode 100644 index 00000000..4ccd86cb --- /dev/null +++ b/jslib/common/src/models/response/twoFactorProviderResponse.ts @@ -0,0 +1,14 @@ +import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; + +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorProviderResponse extends BaseResponse { + enabled: boolean; + type: TwoFactorProviderType; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.type = this.getResponseProperty("Type"); + } +} diff --git a/jslib/common/src/models/response/twoFactorRescoverResponse.ts b/jslib/common/src/models/response/twoFactorRescoverResponse.ts new file mode 100644 index 00000000..0e26db9b --- /dev/null +++ b/jslib/common/src/models/response/twoFactorRescoverResponse.ts @@ -0,0 +1,10 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorRecoverResponse extends BaseResponse { + code: string; + + constructor(response: any) { + super(response); + this.code = this.getResponseProperty("Code"); + } +} diff --git a/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts b/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts new file mode 100644 index 00000000..21ab66a0 --- /dev/null +++ b/jslib/common/src/models/response/twoFactorWebAuthnResponse.ts @@ -0,0 +1,60 @@ +import { Utils } from "../../misc/utils"; + +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorWebAuthnResponse extends BaseResponse { + enabled: boolean; + keys: KeyResponse[]; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + const keys = this.getResponseProperty("Keys"); + this.keys = keys == null ? null : keys.map((k: any) => new KeyResponse(k)); + } +} + +export class KeyResponse extends BaseResponse { + name: string; + id: number; + migrated: boolean; + + constructor(response: any) { + super(response); + this.name = this.getResponseProperty("Name"); + this.id = this.getResponseProperty("Id"); + this.migrated = this.getResponseProperty("Migrated"); + } +} + +export class ChallengeResponse extends BaseResponse implements PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; + + constructor(response: any) { + super(response); + this.attestation = this.getResponseProperty("attestation"); + this.authenticatorSelection = this.getResponseProperty("authenticatorSelection"); + this.challenge = Utils.fromUrlB64ToArray(this.getResponseProperty("challenge")); + this.excludeCredentials = this.getResponseProperty("excludeCredentials").map((c: any) => { + c.id = Utils.fromUrlB64ToArray(c.id).buffer; + return c; + }); + this.extensions = this.getResponseProperty("extensions"); + this.pubKeyCredParams = this.getResponseProperty("pubKeyCredParams"); + this.rp = this.getResponseProperty("rp"); + this.timeout = this.getResponseProperty("timeout"); + + const user = this.getResponseProperty("user"); + user.id = Utils.fromUrlB64ToArray(user.id); + + this.user = user; + } +} diff --git a/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts b/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts new file mode 100644 index 00000000..ee75074b --- /dev/null +++ b/jslib/common/src/models/response/twoFactorYubiKeyResponse.ts @@ -0,0 +1,22 @@ +import { BaseResponse } from "./baseResponse"; + +export class TwoFactorYubiKeyResponse extends BaseResponse { + enabled: boolean; + key1: string; + key2: string; + key3: string; + key4: string; + key5: string; + nfc: boolean; + + constructor(response: any) { + super(response); + this.enabled = this.getResponseProperty("Enabled"); + this.key1 = this.getResponseProperty("Key1"); + this.key2 = this.getResponseProperty("Key2"); + this.key3 = this.getResponseProperty("Key3"); + this.key4 = this.getResponseProperty("Key4"); + this.key5 = this.getResponseProperty("Key5"); + this.nfc = this.getResponseProperty("Nfc"); + } +} diff --git a/jslib/common/src/models/response/userKeyResponse.ts b/jslib/common/src/models/response/userKeyResponse.ts new file mode 100644 index 00000000..5550abdf --- /dev/null +++ b/jslib/common/src/models/response/userKeyResponse.ts @@ -0,0 +1,12 @@ +import { BaseResponse } from "./baseResponse"; + +export class UserKeyResponse extends BaseResponse { + userId: string; + publicKey: string; + + constructor(response: any) { + super(response); + this.userId = this.getResponseProperty("UserId"); + this.publicKey = this.getResponseProperty("PublicKey"); + } +} diff --git a/jslib/common/src/models/view/attachmentView.ts b/jslib/common/src/models/view/attachmentView.ts new file mode 100644 index 00000000..4ebbec7e --- /dev/null +++ b/jslib/common/src/models/view/attachmentView.ts @@ -0,0 +1,35 @@ +import { Attachment } from "../domain/attachment"; +import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey"; + +import { View } from "./view"; + +export class AttachmentView implements View { + id: string = null; + url: string = null; + size: string = null; + sizeName: string = null; + fileName: string = null; + key: SymmetricCryptoKey = null; + + constructor(a?: Attachment) { + if (!a) { + return; + } + + this.id = a.id; + this.url = a.url; + this.size = a.size; + this.sizeName = a.sizeName; + } + + get fileSize(): number { + try { + if (this.size != null) { + return parseInt(this.size, null); + } + } catch { + // Invalid file size. + } + return 0; + } +} diff --git a/jslib/common/src/models/view/cardView.ts b/jslib/common/src/models/view/cardView.ts new file mode 100644 index 00000000..769fa440 --- /dev/null +++ b/jslib/common/src/models/view/cardView.ts @@ -0,0 +1,82 @@ +import { CardLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; + +import { ItemView } from "./itemView"; + +export class CardView extends ItemView { + @linkedFieldOption(LinkedId.CardholderName) + cardholderName: string = null; + @linkedFieldOption(LinkedId.ExpMonth, "expirationMonth") + expMonth: string = null; + @linkedFieldOption(LinkedId.ExpYear, "expirationYear") + expYear: string = null; + @linkedFieldOption(LinkedId.Code, "securityCode") + code: string = null; + + private _brand: string = null; + private _number: string = null; + private _subTitle: string = null; + + constructor() { + super(); + } + + get maskedCode(): string { + return this.code != null ? "•".repeat(this.code.length) : null; + } + + get maskedNumber(): string { + return this.number != null ? "•".repeat(this.number.length) : null; + } + + @linkedFieldOption(LinkedId.Brand) + get brand(): string { + return this._brand; + } + set brand(value: string) { + this._brand = value; + this._subTitle = null; + } + + @linkedFieldOption(LinkedId.Number) + get number(): string { + return this._number; + } + set number(value: string) { + this._number = value; + this._subTitle = null; + } + + get subTitle(): string { + if (this._subTitle == null) { + this._subTitle = this.brand; + if (this.number != null && this.number.length >= 4) { + if (this._subTitle != null && this._subTitle !== "") { + this._subTitle += ", "; + } else { + this._subTitle = ""; + } + + // Show last 5 on amex, last 4 for all others + const count = + this.number.length >= 5 && this.number.match(new RegExp("^3[47]")) != null ? 5 : 4; + this._subTitle += "*" + this.number.substr(this.number.length - count); + } + } + return this._subTitle; + } + + get expiration(): string { + if (!this.expMonth && !this.expYear) { + return null; + } + + let exp = this.expMonth != null ? ("0" + this.expMonth).slice(-2) : "__"; + exp += " / " + (this.expYear != null ? this.formatYear(this.expYear) : "____"); + return exp; + } + + private formatYear(year: string): string { + return year.length === 2 ? "20" + year : year; + } +} diff --git a/jslib/common/src/models/view/cipherView.ts b/jslib/common/src/models/view/cipherView.ts new file mode 100644 index 00000000..3d1f16c1 --- /dev/null +++ b/jslib/common/src/models/view/cipherView.ts @@ -0,0 +1,134 @@ +import { CipherRepromptType } from "../../enums/cipherRepromptType"; +import { CipherType } from "../../enums/cipherType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { Cipher } from "../domain/cipher"; + +import { AttachmentView } from "./attachmentView"; +import { CardView } from "./cardView"; +import { FieldView } from "./fieldView"; +import { IdentityView } from "./identityView"; +import { LoginView } from "./loginView"; +import { PasswordHistoryView } from "./passwordHistoryView"; +import { SecureNoteView } from "./secureNoteView"; +import { View } from "./view"; + +export class CipherView implements View { + id: string = null; + organizationId: string = null; + folderId: string = null; + name: string = null; + notes: string = null; + type: CipherType = null; + favorite = false; + organizationUseTotp = false; + edit = false; + viewPassword = true; + localData: any; + login = new LoginView(); + identity = new IdentityView(); + card = new CardView(); + secureNote = new SecureNoteView(); + attachments: AttachmentView[] = null; + fields: FieldView[] = null; + passwordHistory: PasswordHistoryView[] = null; + collectionIds: string[] = null; + revisionDate: Date = null; + deletedDate: Date = null; + reprompt: CipherRepromptType = CipherRepromptType.None; + + constructor(c?: Cipher) { + if (!c) { + return; + } + + this.id = c.id; + this.organizationId = c.organizationId; + this.folderId = c.folderId; + this.favorite = c.favorite; + this.organizationUseTotp = c.organizationUseTotp; + this.edit = c.edit; + this.viewPassword = c.viewPassword; + this.type = c.type; + this.localData = c.localData; + this.collectionIds = c.collectionIds; + this.revisionDate = c.revisionDate; + this.deletedDate = c.deletedDate; + // Old locally stored ciphers might have reprompt == null. If so set it to None. + this.reprompt = c.reprompt ?? CipherRepromptType.None; + } + + private get item() { + switch (this.type) { + case CipherType.Login: + return this.login; + case CipherType.SecureNote: + return this.secureNote; + case CipherType.Card: + return this.card; + case CipherType.Identity: + return this.identity; + default: + break; + } + + return null; + } + + get subTitle(): string { + return this.item.subTitle; + } + + get hasPasswordHistory(): boolean { + return this.passwordHistory && this.passwordHistory.length > 0; + } + + get hasAttachments(): boolean { + return this.attachments && this.attachments.length > 0; + } + + get hasOldAttachments(): boolean { + if (this.hasAttachments) { + for (let i = 0; i < this.attachments.length; i++) { + if (this.attachments[i].key == null) { + return true; + } + } + } + return false; + } + + get hasFields(): boolean { + return this.fields && this.fields.length > 0; + } + + get passwordRevisionDisplayDate(): Date { + if (this.type !== CipherType.Login || this.login == null) { + return null; + } else if (this.login.password == null || this.login.password === "") { + return null; + } + return this.login.passwordRevisionDate; + } + + get isDeleted(): boolean { + return this.deletedDate != null; + } + + get linkedFieldOptions() { + return this.item.linkedFieldOptions; + } + + linkedFieldValue(id: LinkedIdType) { + const linkedFieldOption = this.linkedFieldOptions?.get(id); + if (linkedFieldOption == null) { + return null; + } + + const item = this.item; + return this.item[linkedFieldOption.propertyKey as keyof typeof item]; + } + + linkedFieldI18nKey(id: LinkedIdType): string { + return this.linkedFieldOptions.get(id)?.i18nKey; + } +} diff --git a/jslib/common/src/models/view/collectionView.ts b/jslib/common/src/models/view/collectionView.ts new file mode 100644 index 00000000..d230e591 --- /dev/null +++ b/jslib/common/src/models/view/collectionView.ts @@ -0,0 +1,28 @@ +import { Collection } from "../domain/collection"; +import { ITreeNodeObject } from "../domain/treeNode"; +import { CollectionGroupDetailsResponse } from "../response/collectionResponse"; + +import { View } from "./view"; + +export class CollectionView implements View, ITreeNodeObject { + id: string = null; + organizationId: string = null; + name: string = null; + externalId: string = null; + readOnly: boolean = null; + hidePasswords: boolean = null; + + constructor(c?: Collection | CollectionGroupDetailsResponse) { + if (!c) { + return; + } + + this.id = c.id; + this.organizationId = c.organizationId; + this.externalId = c.externalId; + if (c instanceof Collection) { + this.readOnly = c.readOnly; + this.hidePasswords = c.hidePasswords; + } + } +} diff --git a/jslib/common/src/models/view/eventView.ts b/jslib/common/src/models/view/eventView.ts new file mode 100644 index 00000000..17339ecb --- /dev/null +++ b/jslib/common/src/models/view/eventView.ts @@ -0,0 +1,27 @@ +import { EventType } from "../../enums/eventType"; + +export class EventView { + message: string; + humanReadableMessage: string; + appIcon: string; + appName: string; + userId: string; + userName: string; + userEmail: string; + date: string; + ip: string; + type: EventType; + + constructor(data: Required) { + this.message = data.message; + this.humanReadableMessage = data.humanReadableMessage; + this.appIcon = data.appIcon; + this.appName = data.appName; + this.userId = data.userId; + this.userName = data.userName; + this.userEmail = data.userEmail; + this.date = data.date; + this.ip = data.ip; + this.type = data.type; + } +} diff --git a/jslib/common/src/models/view/fieldView.ts b/jslib/common/src/models/view/fieldView.ts new file mode 100644 index 00000000..51e3aaef --- /dev/null +++ b/jslib/common/src/models/view/fieldView.ts @@ -0,0 +1,28 @@ +import { FieldType } from "../../enums/fieldType"; +import { LinkedIdType } from "../../enums/linkedIdType"; +import { Field } from "../domain/field"; + +import { View } from "./view"; + +export class FieldView implements View { + name: string = null; + value: string = null; + type: FieldType = null; + newField = false; // Marks if the field is new and hasn't been saved + showValue = false; + showCount = false; + linkedId: LinkedIdType = null; + + constructor(f?: Field) { + if (!f) { + return; + } + + this.type = f.type; + this.linkedId = f.linkedId; + } + + get maskedValue(): string { + return this.value != null ? "••••••••" : null; + } +} diff --git a/jslib/common/src/models/view/folderView.ts b/jslib/common/src/models/view/folderView.ts new file mode 100644 index 00000000..731acffb --- /dev/null +++ b/jslib/common/src/models/view/folderView.ts @@ -0,0 +1,19 @@ +import { Folder } from "../domain/folder"; +import { ITreeNodeObject } from "../domain/treeNode"; + +import { View } from "./view"; + +export class FolderView implements View, ITreeNodeObject { + id: string = null; + name: string = null; + revisionDate: Date = null; + + constructor(f?: Folder) { + if (!f) { + return; + } + + this.id = f.id; + this.revisionDate = f.revisionDate; + } +} diff --git a/jslib/common/src/models/view/identityView.ts b/jslib/common/src/models/view/identityView.ts new file mode 100644 index 00000000..26305636 --- /dev/null +++ b/jslib/common/src/models/view/identityView.ts @@ -0,0 +1,142 @@ +import { IdentityLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; +import { Utils } from "../../misc/utils"; + +import { ItemView } from "./itemView"; + +export class IdentityView extends ItemView { + @linkedFieldOption(LinkedId.Title) + title: string = null; + @linkedFieldOption(LinkedId.MiddleName) + middleName: string = null; + @linkedFieldOption(LinkedId.Address1) + address1: string = null; + @linkedFieldOption(LinkedId.Address2) + address2: string = null; + @linkedFieldOption(LinkedId.Address3) + address3: string = null; + @linkedFieldOption(LinkedId.City, "cityTown") + city: string = null; + @linkedFieldOption(LinkedId.State, "stateProvince") + state: string = null; + @linkedFieldOption(LinkedId.PostalCode, "zipPostalCode") + postalCode: string = null; + @linkedFieldOption(LinkedId.Country) + country: string = null; + @linkedFieldOption(LinkedId.Company) + company: string = null; + @linkedFieldOption(LinkedId.Email) + email: string = null; + @linkedFieldOption(LinkedId.Phone) + phone: string = null; + @linkedFieldOption(LinkedId.Ssn) + ssn: string = null; + @linkedFieldOption(LinkedId.Username) + username: string = null; + @linkedFieldOption(LinkedId.PassportNumber) + passportNumber: string = null; + @linkedFieldOption(LinkedId.LicenseNumber) + licenseNumber: string = null; + + private _firstName: string = null; + private _lastName: string = null; + private _subTitle: string = null; + + constructor() { + super(); + } + + @linkedFieldOption(LinkedId.FirstName) + get firstName(): string { + return this._firstName; + } + set firstName(value: string) { + this._firstName = value; + this._subTitle = null; + } + + @linkedFieldOption(LinkedId.LastName) + get lastName(): string { + return this._lastName; + } + set lastName(value: string) { + this._lastName = value; + this._subTitle = null; + } + + get subTitle(): string { + if (this._subTitle == null && (this.firstName != null || this.lastName != null)) { + this._subTitle = ""; + if (this.firstName != null) { + this._subTitle = this.firstName; + } + if (this.lastName != null) { + if (this._subTitle !== "") { + this._subTitle += " "; + } + this._subTitle += this.lastName; + } + } + + return this._subTitle; + } + + @linkedFieldOption(LinkedId.FullName) + get fullName(): string { + if ( + this.title != null || + this.firstName != null || + this.middleName != null || + this.lastName != null + ) { + let name = ""; + if (this.title != null) { + name += this.title + " "; + } + if (this.firstName != null) { + name += this.firstName + " "; + } + if (this.middleName != null) { + name += this.middleName + " "; + } + if (this.lastName != null) { + name += this.lastName; + } + return name.trim(); + } + + return null; + } + + get fullAddress(): string { + let address = this.address1; + if (!Utils.isNullOrWhitespace(this.address2)) { + if (!Utils.isNullOrWhitespace(address)) { + address += ", "; + } + address += this.address2; + } + if (!Utils.isNullOrWhitespace(this.address3)) { + if (!Utils.isNullOrWhitespace(address)) { + address += ", "; + } + address += this.address3; + } + return address; + } + + get fullAddressPart2(): string { + if (this.city == null && this.state == null && this.postalCode == null) { + return null; + } + const city = this.city || "-"; + const state = this.state; + const postalCode = this.postalCode || "-"; + let addressPart2 = city; + if (!Utils.isNullOrWhitespace(state)) { + addressPart2 += ", " + state; + } + addressPart2 += ", " + postalCode; + return addressPart2; + } +} diff --git a/jslib/common/src/models/view/itemView.ts b/jslib/common/src/models/view/itemView.ts new file mode 100644 index 00000000..3a557ffa --- /dev/null +++ b/jslib/common/src/models/view/itemView.ts @@ -0,0 +1,8 @@ +import { LinkedMetadata } from "../../misc/linkedFieldOption.decorator"; + +import { View } from "./view"; + +export abstract class ItemView implements View { + linkedFieldOptions: Map; + abstract get subTitle(): string; +} diff --git a/jslib/common/src/models/view/loginUriView.ts b/jslib/common/src/models/view/loginUriView.ts new file mode 100644 index 00000000..cca9520d --- /dev/null +++ b/jslib/common/src/models/view/loginUriView.ts @@ -0,0 +1,127 @@ +import { UriMatchType } from "../../enums/uriMatchType"; +import { Utils } from "../../misc/utils"; +import { LoginUri } from "../domain/loginUri"; + +import { View } from "./view"; + +const CanLaunchWhitelist = [ + "https://", + "http://", + "ssh://", + "ftp://", + "sftp://", + "irc://", + "vnc://", + // https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-uri + "rdp://", // Legacy RDP URI scheme + "ms-rd:", // Preferred RDP URI scheme + "chrome://", + "iosapp://", + "androidapp://", +]; + +export class LoginUriView implements View { + match: UriMatchType = null; + + private _uri: string = null; + private _domain: string = null; + private _hostname: string = null; + private _host: string = null; + private _canLaunch: boolean = null; + + constructor(u?: LoginUri) { + if (!u) { + return; + } + + this.match = u.match; + } + + get uri(): string { + return this._uri; + } + set uri(value: string) { + this._uri = value; + this._domain = null; + this._canLaunch = null; + } + + get domain(): string { + if (this._domain == null && this.uri != null) { + this._domain = Utils.getDomain(this.uri); + if (this._domain === "") { + this._domain = null; + } + } + + return this._domain; + } + + get hostname(): string { + if (this.match === UriMatchType.RegularExpression) { + return null; + } + if (this._hostname == null && this.uri != null) { + this._hostname = Utils.getHostname(this.uri); + if (this._hostname === "") { + this._hostname = null; + } + } + + return this._hostname; + } + + get host(): string { + if (this.match === UriMatchType.RegularExpression) { + return null; + } + if (this._host == null && this.uri != null) { + this._host = Utils.getHost(this.uri); + if (this._host === "") { + this._host = null; + } + } + + return this._host; + } + + get hostnameOrUri(): string { + return this.hostname != null ? this.hostname : this.uri; + } + + get hostOrUri(): string { + return this.host != null ? this.host : this.uri; + } + + get isWebsite(): boolean { + return ( + this.uri != null && + (this.uri.indexOf("http://") === 0 || + this.uri.indexOf("https://") === 0 || + (this.uri.indexOf("://") < 0 && Utils.tldEndingRegex.test(this.uri))) + ); + } + + get canLaunch(): boolean { + if (this._canLaunch != null) { + return this._canLaunch; + } + if (this.uri != null && this.match !== UriMatchType.RegularExpression) { + const uri = this.launchUri; + for (let i = 0; i < CanLaunchWhitelist.length; i++) { + if (uri.indexOf(CanLaunchWhitelist[i]) === 0) { + this._canLaunch = true; + return this._canLaunch; + } + } + } + this._canLaunch = false; + return this._canLaunch; + } + + get launchUri(): string { + return this.uri.indexOf("://") < 0 && Utils.tldEndingRegex.test(this.uri) + ? "http://" + this.uri + : this.uri; + } +} diff --git a/jslib/common/src/models/view/loginView.ts b/jslib/common/src/models/view/loginView.ts new file mode 100644 index 00000000..e86dd14b --- /dev/null +++ b/jslib/common/src/models/view/loginView.ts @@ -0,0 +1,63 @@ +import { LoginLinkedId as LinkedId } from "../../enums/linkedIdType"; +import { linkedFieldOption } from "../../misc/linkedFieldOption.decorator"; +import { Utils } from "../../misc/utils"; +import { Login } from "../domain/login"; + +import { ItemView } from "./itemView"; +import { LoginUriView } from "./loginUriView"; + +export class LoginView extends ItemView { + @linkedFieldOption(LinkedId.Username) + username: string = null; + @linkedFieldOption(LinkedId.Password) + password: string = null; + + passwordRevisionDate?: Date = null; + totp: string = null; + uris: LoginUriView[] = null; + autofillOnPageLoad: boolean = null; + + constructor(l?: Login) { + super(); + if (!l) { + return; + } + + this.passwordRevisionDate = l.passwordRevisionDate; + this.autofillOnPageLoad = l.autofillOnPageLoad; + } + + get uri(): string { + return this.hasUris ? this.uris[0].uri : null; + } + + get maskedPassword(): string { + return this.password != null ? "••••••••" : null; + } + + get subTitle(): string { + return this.username; + } + + get canLaunch(): boolean { + return this.hasUris && this.uris.some((u) => u.canLaunch); + } + + get hasTotp(): boolean { + return !Utils.isNullOrWhitespace(this.totp); + } + + get launchUri(): string { + if (this.hasUris) { + const uri = this.uris.find((u) => u.canLaunch); + if (uri != null) { + return uri.launchUri; + } + } + return null; + } + + get hasUris(): boolean { + return this.uris != null && this.uris.length > 0; + } +} diff --git a/jslib/common/src/models/view/passwordHistoryView.ts b/jslib/common/src/models/view/passwordHistoryView.ts new file mode 100644 index 00000000..aa780079 --- /dev/null +++ b/jslib/common/src/models/view/passwordHistoryView.ts @@ -0,0 +1,16 @@ +import { Password } from "../domain/password"; + +import { View } from "./view"; + +export class PasswordHistoryView implements View { + password: string = null; + lastUsedDate: Date = null; + + constructor(ph?: Password) { + if (!ph) { + return; + } + + this.lastUsedDate = ph.lastUsedDate; + } +} diff --git a/jslib/common/src/models/view/secureNoteView.ts b/jslib/common/src/models/view/secureNoteView.ts new file mode 100644 index 00000000..c324b07e --- /dev/null +++ b/jslib/common/src/models/view/secureNoteView.ts @@ -0,0 +1,21 @@ +import { SecureNoteType } from "../../enums/secureNoteType"; +import { SecureNote } from "../domain/secureNote"; + +import { ItemView } from "./itemView"; + +export class SecureNoteView extends ItemView { + type: SecureNoteType = null; + + constructor(n?: SecureNote) { + super(); + if (!n) { + return; + } + + this.type = n.type; + } + + get subTitle(): string { + return null; + } +} diff --git a/jslib/common/src/models/view/sendAccessView.ts b/jslib/common/src/models/view/sendAccessView.ts new file mode 100644 index 00000000..71e5276a --- /dev/null +++ b/jslib/common/src/models/view/sendAccessView.ts @@ -0,0 +1,27 @@ +import { SendType } from "../../enums/sendType"; +import { SendAccess } from "../domain/sendAccess"; + +import { SendFileView } from "./sendFileView"; +import { SendTextView } from "./sendTextView"; +import { View } from "./view"; + +export class SendAccessView implements View { + id: string = null; + name: string = null; + type: SendType = null; + text = new SendTextView(); + file = new SendFileView(); + expirationDate: Date = null; + creatorIdentifier: string = null; + + constructor(s?: SendAccess) { + if (!s) { + return; + } + + this.id = s.id; + this.type = s.type; + this.expirationDate = s.expirationDate; + this.creatorIdentifier = s.creatorIdentifier; + } +} diff --git a/jslib/common/src/models/view/sendFileView.ts b/jslib/common/src/models/view/sendFileView.ts new file mode 100644 index 00000000..7d37f2de --- /dev/null +++ b/jslib/common/src/models/view/sendFileView.ts @@ -0,0 +1,31 @@ +import { SendFile } from "../domain/sendFile"; + +import { View } from "./view"; + +export class SendFileView implements View { + id: string = null; + size: string = null; + sizeName: string = null; + fileName: string = null; + + constructor(f?: SendFile) { + if (!f) { + return; + } + + this.id = f.id; + this.size = f.size; + this.sizeName = f.sizeName; + } + + get fileSize(): number { + try { + if (this.size != null) { + return parseInt(this.size, null); + } + } catch { + // Invalid file size. + } + return 0; + } +} diff --git a/jslib/common/src/models/view/sendTextView.ts b/jslib/common/src/models/view/sendTextView.ts new file mode 100644 index 00000000..a2c6b571 --- /dev/null +++ b/jslib/common/src/models/view/sendTextView.ts @@ -0,0 +1,20 @@ +import { SendText } from "../domain/sendText"; + +import { View } from "./view"; + +export class SendTextView implements View { + text: string = null; + hidden: boolean; + + constructor(t?: SendText) { + if (!t) { + return; + } + + this.hidden = t.hidden; + } + + get maskedText(): string { + return this.text != null ? "••••••••" : null; + } +} diff --git a/jslib/common/src/models/view/sendView.ts b/jslib/common/src/models/view/sendView.ts new file mode 100644 index 00000000..1a2a214f --- /dev/null +++ b/jslib/common/src/models/view/sendView.ts @@ -0,0 +1,68 @@ +import { SendType } from "../../enums/sendType"; +import { Utils } from "../../misc/utils"; +import { Send } from "../domain/send"; +import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey"; + +import { SendFileView } from "./sendFileView"; +import { SendTextView } from "./sendTextView"; +import { View } from "./view"; + +export class SendView implements View { + id: string = null; + accessId: string = null; + name: string = null; + notes: string = null; + key: ArrayBuffer; + cryptoKey: SymmetricCryptoKey; + type: SendType = null; + text = new SendTextView(); + file = new SendFileView(); + maxAccessCount?: number = null; + accessCount = 0; + revisionDate: Date = null; + deletionDate: Date = null; + expirationDate: Date = null; + password: string = null; + disabled = false; + hideEmail = false; + + constructor(s?: Send) { + if (!s) { + return; + } + + this.id = s.id; + this.accessId = s.accessId; + this.type = s.type; + this.maxAccessCount = s.maxAccessCount; + this.accessCount = s.accessCount; + this.revisionDate = s.revisionDate; + this.deletionDate = s.deletionDate; + this.expirationDate = s.expirationDate; + this.disabled = s.disabled; + this.password = s.password; + this.hideEmail = s.hideEmail; + } + + get urlB64Key(): string { + return Utils.fromBufferToUrlB64(this.key); + } + + get maxAccessCountReached(): boolean { + if (this.maxAccessCount == null) { + return false; + } + return this.accessCount >= this.maxAccessCount; + } + + get expired(): boolean { + if (this.expirationDate == null) { + return false; + } + return this.expirationDate <= new Date(); + } + + get pendingDelete(): boolean { + return this.deletionDate <= new Date(); + } +} diff --git a/jslib/common/src/models/view/ssoConfigView.ts b/jslib/common/src/models/view/ssoConfigView.ts new file mode 100644 index 00000000..b4784c36 --- /dev/null +++ b/jslib/common/src/models/view/ssoConfigView.ts @@ -0,0 +1,104 @@ +import { + OpenIdConnectRedirectBehavior, + Saml2BindingType, + Saml2NameIdFormat, + Saml2SigningBehavior, + SsoType, +} from "../../enums/ssoEnums"; +import { SsoConfigApi } from "../api/ssoConfigApi"; + +import { View } from "./view"; + +export class SsoConfigView extends View { + configType: SsoType; + + keyConnectorEnabled: boolean; + keyConnectorUrl: string; + + openId: { + authority: string; + clientId: string; + clientSecret: string; + metadataAddress: string; + redirectBehavior: OpenIdConnectRedirectBehavior; + getClaimsFromUserInfoEndpoint: boolean; + additionalScopes: string; + additionalUserIdClaimTypes: string; + additionalEmailClaimTypes: string; + additionalNameClaimTypes: string; + acrValues: string; + expectedReturnAcrValue: string; + }; + + saml: { + spNameIdFormat: Saml2NameIdFormat; + spOutboundSigningAlgorithm: string; + spSigningBehavior: Saml2SigningBehavior; + spMinIncomingSigningAlgorithm: boolean; + spWantAssertionsSigned: boolean; + spValidateCertificates: boolean; + + idpEntityId: string; + idpBindingType: Saml2BindingType; + idpSingleSignOnServiceUrl: string; + idpSingleLogoutServiceUrl: string; + idpX509PublicCert: string; + idpOutboundSigningAlgorithm: string; + idpAllowUnsolicitedAuthnResponse: boolean; + idpAllowOutboundLogoutRequests: boolean; + idpWantAuthnRequestsSigned: boolean; + }; + + constructor(api: SsoConfigApi) { + super(); + if (api == null) { + return; + } + + this.configType = api.configType; + + this.keyConnectorEnabled = api.keyConnectorEnabled; + this.keyConnectorUrl = api.keyConnectorUrl; + + if (this.configType === SsoType.OpenIdConnect) { + this.openId = { + authority: api.authority, + clientId: api.clientId, + clientSecret: api.clientSecret, + metadataAddress: api.metadataAddress, + redirectBehavior: api.redirectBehavior, + getClaimsFromUserInfoEndpoint: api.getClaimsFromUserInfoEndpoint, + additionalScopes: api.additionalScopes, + additionalUserIdClaimTypes: api.additionalUserIdClaimTypes, + additionalEmailClaimTypes: api.additionalEmailClaimTypes, + additionalNameClaimTypes: api.additionalNameClaimTypes, + acrValues: api.acrValues, + expectedReturnAcrValue: api.expectedReturnAcrValue, + }; + } else if (this.configType === SsoType.Saml2) { + this.saml = { + spNameIdFormat: api.spNameIdFormat, + spOutboundSigningAlgorithm: api.spOutboundSigningAlgorithm, + spSigningBehavior: api.spSigningBehavior, + spMinIncomingSigningAlgorithm: api.spMinIncomingSigningAlgorithm, + spWantAssertionsSigned: api.spWantAssertionsSigned, + spValidateCertificates: api.spValidateCertificates, + + idpEntityId: api.idpEntityId, + idpBindingType: api.idpBindingType, + idpSingleSignOnServiceUrl: api.idpSingleSignOnServiceUrl, + idpSingleLogoutServiceUrl: api.idpSingleLogoutServiceUrl, + idpX509PublicCert: api.idpX509PublicCert, + idpOutboundSigningAlgorithm: api.idpOutboundSigningAlgorithm, + idpAllowUnsolicitedAuthnResponse: api.idpAllowUnsolicitedAuthnResponse, + idpWantAuthnRequestsSigned: api.idpWantAuthnRequestsSigned, + + // Value is inverted in the view model (allow instead of disable) + idpAllowOutboundLogoutRequests: + api.idpDisableOutboundLogoutRequests == null + ? null + : !api.idpDisableOutboundLogoutRequests, + }; + } + } +} diff --git a/jslib/common/src/models/view/view.ts b/jslib/common/src/models/view/view.ts new file mode 100644 index 00000000..1f16b3d5 --- /dev/null +++ b/jslib/common/src/models/view/view.ts @@ -0,0 +1 @@ +export class View {} diff --git a/jslib/common/src/services/api.service.ts b/jslib/common/src/services/api.service.ts new file mode 100644 index 00000000..da2fcd5d --- /dev/null +++ b/jslib/common/src/services/api.service.ts @@ -0,0 +1,2534 @@ +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { DeviceRequest } from "jslib-common/models/request/deviceRequest"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; + +import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { TokenService } from "../abstractions/token.service"; +import { DeviceType } from "../enums/deviceType"; +import { PolicyType } from "../enums/policyType"; +import { Utils } from "../misc/utils"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { DeleteRecoverRequest } from "../models/request/deleteRecoverRequest"; +import { EmailRequest } from "../models/request/emailRequest"; +import { EmailTokenRequest } from "../models/request/emailTokenRequest"; +import { EmergencyAccessAcceptRequest } from "../models/request/emergencyAccessAcceptRequest"; +import { EmergencyAccessConfirmRequest } from "../models/request/emergencyAccessConfirmRequest"; +import { EmergencyAccessInviteRequest } from "../models/request/emergencyAccessInviteRequest"; +import { EmergencyAccessPasswordRequest } from "../models/request/emergencyAccessPasswordRequest"; +import { EmergencyAccessUpdateRequest } from "../models/request/emergencyAccessUpdateRequest"; +import { EventRequest } from "../models/request/eventRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { GroupRequest } from "../models/request/groupRequest"; +import { IapCheckRequest } from "../models/request/iapCheckRequest"; +import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest"; +import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest"; +import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KdfRequest } from "../models/request/kdfRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest"; +import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest"; +import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest"; +import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest"; +import { OrganizationImportRequest } from "../models/request/organizationImportRequest"; +import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest"; +import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest"; +import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest"; +import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest"; +import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest"; +import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest"; +import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest"; +import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest"; +import { OrganizationUserConfirmRequest } from "../models/request/organizationUserConfirmRequest"; +import { OrganizationUserInviteRequest } from "../models/request/organizationUserInviteRequest"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organizationUserResetPasswordEnrollmentRequest"; +import { OrganizationUserResetPasswordRequest } from "../models/request/organizationUserResetPasswordRequest"; +import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizationUserUpdateGroupsRequest"; +import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; +import { PasswordHintRequest } from "../models/request/passwordHintRequest"; +import { PasswordRequest } from "../models/request/passwordRequest"; +import { PaymentRequest } from "../models/request/paymentRequest"; +import { PolicyRequest } from "../models/request/policyRequest"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; +import { ProviderOrganizationCreateRequest } from "../models/request/provider/providerOrganizationCreateRequest"; +import { ProviderSetupRequest } from "../models/request/provider/providerSetupRequest"; +import { ProviderUpdateRequest } from "../models/request/provider/providerUpdateRequest"; +import { ProviderUserAcceptRequest } from "../models/request/provider/providerUserAcceptRequest"; +import { ProviderUserBulkConfirmRequest } from "../models/request/provider/providerUserBulkConfirmRequest"; +import { ProviderUserBulkRequest } from "../models/request/provider/providerUserBulkRequest"; +import { ProviderUserConfirmRequest } from "../models/request/provider/providerUserConfirmRequest"; +import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest"; +import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest"; +import { RegisterRequest } from "../models/request/registerRequest"; +import { SeatRequest } from "../models/request/seatRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest"; +import { SendAccessRequest } from "../models/request/sendAccessRequest"; +import { SendRequest } from "../models/request/sendRequest"; +import { SetPasswordRequest } from "../models/request/setPasswordRequest"; +import { StorageRequest } from "../models/request/storageRequest"; +import { TaxInfoUpdateRequest } from "../models/request/taxInfoUpdateRequest"; +import { TwoFactorEmailRequest } from "../models/request/twoFactorEmailRequest"; +import { TwoFactorProviderRequest } from "../models/request/twoFactorProviderRequest"; +import { TwoFactorRecoveryRequest } from "../models/request/twoFactorRecoveryRequest"; +import { UpdateDomainsRequest } from "../models/request/updateDomainsRequest"; +import { UpdateKeyRequest } from "../models/request/updateKeyRequest"; +import { UpdateProfileRequest } from "../models/request/updateProfileRequest"; +import { UpdateTempPasswordRequest } from "../models/request/updateTempPasswordRequest"; +import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/updateTwoFactorAuthenticatorRequest"; +import { UpdateTwoFactorDuoRequest } from "../models/request/updateTwoFactorDuoRequest"; +import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEmailRequest"; +import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest"; +import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest"; +import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest"; +import { VerifyBankRequest } from "../models/request/verifyBankRequest"; +import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest"; +import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; +import { ApiKeyResponse } from "../models/response/apiKeyResponse"; +import { AttachmentResponse } from "../models/response/attachmentResponse"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { BillingResponse } from "../models/response/billingResponse"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { + CollectionGroupDetailsResponse, + CollectionResponse, +} from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { + EmergencyAccessGranteeDetailsResponse, + EmergencyAccessGrantorDetailsResponse, + EmergencyAccessTakeoverResponse, + EmergencyAccessViewResponse, +} from "../models/response/emergencyAccessResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { EventResponse } from "../models/response/eventResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { GroupDetailsResponse, GroupResponse } from "../models/response/groupResponse"; +import { IdentityCaptchaResponse } from "../models/response/identityCaptchaResponse"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; +import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse"; +import { ListResponse } from "../models/response/listResponse"; +import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse"; +import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse"; +import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse"; +import { OrganizationResponse } from "../models/response/organizationResponse"; +import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse"; +import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse"; +import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse"; +import { + OrganizationUserDetailsResponse, + OrganizationUserResetPasswordDetailsReponse, + OrganizationUserUserDetailsResponse, +} from "../models/response/organizationUserResponse"; +import { PaymentResponse } from "../models/response/paymentResponse"; +import { PlanResponse } from "../models/response/planResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { PreloginResponse } from "../models/response/preloginResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { + ProviderOrganizationOrganizationDetailsResponse, + ProviderOrganizationResponse, +} from "../models/response/provider/providerOrganizationResponse"; +import { ProviderResponse } from "../models/response/provider/providerResponse"; +import { ProviderUserBulkPublicKeyResponse } from "../models/response/provider/providerUserBulkPublicKeyResponse"; +import { ProviderUserBulkResponse } from "../models/response/provider/providerUserBulkResponse"; +import { + ProviderUserResponse, + ProviderUserUserDetailsResponse, +} from "../models/response/provider/providerUserResponse"; +import { SelectionReadOnlyResponse } from "../models/response/selectionReadOnlyResponse"; +import { SendAccessResponse } from "../models/response/sendAccessResponse"; +import { SendFileDownloadDataResponse } from "../models/response/sendFileDownloadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SubscriptionResponse } from "../models/response/subscriptionResponse"; +import { SyncResponse } from "../models/response/syncResponse"; +import { TaxInfoResponse } from "../models/response/taxInfoResponse"; +import { TaxRateResponse } from "../models/response/taxRateResponse"; +import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse"; +import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse"; +import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse"; +import { TwoFactorProviderResponse } from "../models/response/twoFactorProviderResponse"; +import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse"; +import { + TwoFactorWebAuthnResponse, + ChallengeResponse, +} from "../models/response/twoFactorWebAuthnResponse"; +import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse"; +import { UserKeyResponse } from "../models/response/userKeyResponse"; +import { SendAccessView } from "../models/view/sendAccessView"; + +export class ApiService implements ApiServiceAbstraction { + private device: DeviceType; + private deviceType: string; + private isWebClient = false; + private isDesktopClient = false; + + constructor( + private tokenService: TokenService, + private platformUtilsService: PlatformUtilsService, + private environmentService: EnvironmentService, + private appIdService: AppIdService, + private logoutCallback: (expired: boolean) => Promise, + private customUserAgent: string = null + ) { + this.device = platformUtilsService.getDevice(); + this.deviceType = this.device.toString(); + this.isWebClient = + this.device === DeviceType.IEBrowser || + this.device === DeviceType.ChromeBrowser || + this.device === DeviceType.EdgeBrowser || + this.device === DeviceType.FirefoxBrowser || + this.device === DeviceType.OperaBrowser || + this.device === DeviceType.SafariBrowser || + this.device === DeviceType.UnknownBrowser || + this.device === DeviceType.VivaldiBrowser; + this.isDesktopClient = + this.device === DeviceType.WindowsDesktop || + this.device === DeviceType.MacOsDesktop || + this.device === DeviceType.LinuxDesktop; + } + + // Auth APIs + + async postIdentityToken( + request: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest + ): Promise { + const headers = new Headers({ + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + request.alterIdentityTokenHeaders(headers); + + const identityToken = + request instanceof ApiTokenRequest + ? request.toIdentityToken() + : request.toIdentityToken(this.platformUtilsService.getClientType()); + + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + "/connect/token", { + body: this.qsStringify(identityToken), + credentials: this.getCredentials(), + cache: "no-store", + headers: headers, + method: "POST", + }) + ); + + let responseJson: any = null; + if (this.isJsonResponse(response)) { + responseJson = await response.json(); + } + + if (responseJson != null) { + if (response.status === 200) { + return new IdentityTokenResponse(responseJson); + } else if ( + response.status === 400 && + responseJson.TwoFactorProviders2 && + Object.keys(responseJson.TwoFactorProviders2).length + ) { + await this.tokenService.clearTwoFactorToken(); + return new IdentityTwoFactorResponse(responseJson); + } else if ( + response.status === 400 && + responseJson.HCaptcha_SiteKey && + Object.keys(responseJson.HCaptcha_SiteKey).length + ) { + return new IdentityCaptchaResponse(responseJson); + } + } + + return Promise.reject(new ErrorResponse(responseJson, response.status, true)); + } + + async refreshIdentityToken(): Promise { + try { + await this.doAuthRefresh(); + } catch (e) { + return Promise.reject(null); + } + } + + // Account APIs + + async getProfile(): Promise { + const r = await this.send("GET", "/accounts/profile", null, true, true); + return new ProfileResponse(r); + } + + async getUserBilling(): Promise { + const r = await this.send("GET", "/accounts/billing", null, true, true); + return new BillingResponse(r); + } + + async getUserSubscription(): Promise { + const r = await this.send("GET", "/accounts/subscription", null, true, true); + return new SubscriptionResponse(r); + } + + async getTaxInfo(): Promise { + const r = await this.send("GET", "/accounts/tax", null, true, true); + return new TaxInfoResponse(r); + } + + async putProfile(request: UpdateProfileRequest): Promise { + const r = await this.send("PUT", "/accounts/profile", request, true, true); + return new ProfileResponse(r); + } + + putTaxInfo(request: TaxInfoUpdateRequest): Promise { + return this.send("PUT", "/accounts/tax", request, true, false); + } + + async postPrelogin(request: PreloginRequest): Promise { + const r = await this.send( + "POST", + "/accounts/prelogin", + request, + false, + true, + this.platformUtilsService.isDev() + ? this.environmentService.getIdentityUrl() + : this.environmentService.getApiUrl() + ); + return new PreloginResponse(r); + } + + postEmailToken(request: EmailTokenRequest): Promise { + return this.send("POST", "/accounts/email-token", request, true, false); + } + + postEmail(request: EmailRequest): Promise { + return this.send("POST", "/accounts/email", request, true, false); + } + + postPassword(request: PasswordRequest): Promise { + return this.send("POST", "/accounts/password", request, true, false); + } + + setPassword(request: SetPasswordRequest): Promise { + return this.send("POST", "/accounts/set-password", request, true, false); + } + + postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise { + return this.send("POST", "/accounts/set-key-connector-key", request, true, false); + } + + postSecurityStamp(request: SecretVerificationRequest): Promise { + return this.send("POST", "/accounts/security-stamp", request, true, false); + } + + deleteAccount(request: SecretVerificationRequest): Promise { + return this.send("DELETE", "/accounts", request, true, false); + } + + async getAccountRevisionDate(): Promise { + const r = await this.send("GET", "/accounts/revision-date", null, true, true); + return r as number; + } + + postPasswordHint(request: PasswordHintRequest): Promise { + return this.send("POST", "/accounts/password-hint", request, false, false); + } + + postRegister(request: RegisterRequest): Promise { + return this.send( + "POST", + "/accounts/register", + request, + false, + false, + this.platformUtilsService.isDev() + ? this.environmentService.getIdentityUrl() + : this.environmentService.getApiUrl() + ); + } + + async postPremium(data: FormData): Promise { + const r = await this.send("POST", "/accounts/premium", data, true, true); + return new PaymentResponse(r); + } + + async postIapCheck(request: IapCheckRequest): Promise { + return this.send("POST", "/accounts/iap-check", request, true, false); + } + + postReinstatePremium(): Promise { + return this.send("POST", "/accounts/reinstate-premium", null, true, false); + } + + postCancelPremium(): Promise { + return this.send("POST", "/accounts/cancel-premium", null, true, false); + } + + async postAccountStorage(request: StorageRequest): Promise { + const r = await this.send("POST", "/accounts/storage", request, true, true); + return new PaymentResponse(r); + } + + postAccountPayment(request: PaymentRequest): Promise { + return this.send("POST", "/accounts/payment", request, true, false); + } + + postAccountLicense(data: FormData): Promise { + return this.send("POST", "/accounts/license", data, true, false); + } + + postAccountKeys(request: KeysRequest): Promise { + return this.send("POST", "/accounts/keys", request, true, false); + } + + postAccountKey(request: UpdateKeyRequest): Promise { + return this.send("POST", "/accounts/key", request, true, false); + } + + postAccountVerifyEmail(): Promise { + return this.send("POST", "/accounts/verify-email", null, true, false); + } + + postAccountVerifyEmailToken(request: VerifyEmailRequest): Promise { + return this.send("POST", "/accounts/verify-email-token", request, false, false); + } + + postAccountVerifyPassword(request: SecretVerificationRequest): Promise { + return this.send("POST", "/accounts/verify-password", request, true, false); + } + + postAccountRecoverDelete(request: DeleteRecoverRequest): Promise { + return this.send("POST", "/accounts/delete-recover", request, false, false); + } + + postAccountRecoverDeleteToken(request: VerifyDeleteRecoverRequest): Promise { + return this.send("POST", "/accounts/delete-recover-token", request, false, false); + } + + postAccountKdf(request: KdfRequest): Promise { + return this.send("POST", "/accounts/kdf", request, true, false); + } + + async deleteSsoUser(organizationId: string): Promise { + return this.send("DELETE", "/accounts/sso/" + organizationId, null, true, false); + } + + async getSsoUserIdentifier(): Promise { + return this.send("GET", "/accounts/sso/user-identifier", null, true, true); + } + + async postUserApiKey(id: string, request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/accounts/api-key", request, true, true); + return new ApiKeyResponse(r); + } + + async postUserRotateApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/accounts/rotate-api-key", request, true, true); + return new ApiKeyResponse(r); + } + + putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise { + return this.send("PUT", "/accounts/update-temp-password", request, true, false); + } + + postAccountRequestOTP(): Promise { + return this.send("POST", "/accounts/request-otp", null, true, false); + } + + postAccountVerifyOTP(request: VerifyOTPRequest): Promise { + return this.send("POST", "/accounts/verify-otp", request, true, false); + } + + postConvertToKeyConnector(): Promise { + return this.send("POST", "/accounts/convert-to-key-connector", null, true, false); + } + + // Folder APIs + + async getFolder(id: string): Promise { + const r = await this.send("GET", "/folders/" + id, null, true, true); + return new FolderResponse(r); + } + + async postFolder(request: FolderRequest): Promise { + const r = await this.send("POST", "/folders", request, true, true); + return new FolderResponse(r); + } + + async putFolder(id: string, request: FolderRequest): Promise { + const r = await this.send("PUT", "/folders/" + id, request, true, true); + return new FolderResponse(r); + } + + deleteFolder(id: string): Promise { + return this.send("DELETE", "/folders/" + id, null, true, false); + } + + // Send APIs + + async getSend(id: string): Promise { + const r = await this.send("GET", "/sends/" + id, null, true, true); + return new SendResponse(r); + } + + async postSendAccess( + id: string, + request: SendAccessRequest, + apiUrl?: string + ): Promise { + const addSendIdHeader = (headers: Headers) => { + headers.set("Send-Id", id); + }; + const r = await this.send( + "POST", + "/sends/access/" + id, + request, + false, + true, + apiUrl, + addSendIdHeader + ); + return new SendAccessResponse(r); + } + + async getSendFileDownloadData( + send: SendAccessView, + request: SendAccessRequest, + apiUrl?: string + ): Promise { + const addSendIdHeader = (headers: Headers) => { + headers.set("Send-Id", send.id); + }; + const r = await this.send( + "POST", + "/sends/" + send.id + "/access/file/" + send.file.id, + request, + false, + true, + apiUrl, + addSendIdHeader + ); + return new SendFileDownloadDataResponse(r); + } + + async getSends(): Promise> { + const r = await this.send("GET", "/sends", null, true, true); + return new ListResponse(r, SendResponse); + } + + async postSend(request: SendRequest): Promise { + const r = await this.send("POST", "/sends", request, true, true); + return new SendResponse(r); + } + + async postFileTypeSend(request: SendRequest): Promise { + const r = await this.send("POST", "/sends/file/v2", request, true, true); + return new SendFileUploadDataResponse(r); + } + + async renewSendFileUploadUrl( + sendId: string, + fileId: string + ): Promise { + const r = await this.send("GET", "/sends/" + sendId + "/file/" + fileId, null, true, true); + return new SendFileUploadDataResponse(r); + } + + postSendFile(sendId: string, fileId: string, data: FormData): Promise { + return this.send("POST", "/sends/" + sendId + "/file/" + fileId, data, true, false); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postSendFileLegacy(data: FormData): Promise { + const r = await this.send("POST", "/sends/file", data, true, true); + return new SendResponse(r); + } + + async putSend(id: string, request: SendRequest): Promise { + const r = await this.send("PUT", "/sends/" + id, request, true, true); + return new SendResponse(r); + } + + async putSendRemovePassword(id: string): Promise { + const r = await this.send("PUT", "/sends/" + id + "/remove-password", null, true, true); + return new SendResponse(r); + } + + deleteSend(id: string): Promise { + return this.send("DELETE", "/sends/" + id, null, true, false); + } + + // Cipher APIs + + async getCipher(id: string): Promise { + const r = await this.send("GET", "/ciphers/" + id, null, true, true); + return new CipherResponse(r); + } + + async getCipherAdmin(id: string): Promise { + const r = await this.send("GET", "/ciphers/" + id + "/admin", null, true, true); + return new CipherResponse(r); + } + + async getCiphersOrganization(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/ciphers/organization-details?organizationId=" + organizationId, + null, + true, + true + ); + return new ListResponse(r, CipherResponse); + } + + async postCipher(request: CipherRequest): Promise { + const r = await this.send("POST", "/ciphers", request, true, true); + return new CipherResponse(r); + } + + async postCipherCreate(request: CipherCreateRequest): Promise { + const r = await this.send("POST", "/ciphers/create", request, true, true); + return new CipherResponse(r); + } + + async postCipherAdmin(request: CipherCreateRequest): Promise { + const r = await this.send("POST", "/ciphers/admin", request, true, true); + return new CipherResponse(r); + } + + async putCipher(id: string, request: CipherRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id, request, true, true); + return new CipherResponse(r); + } + + async putCipherAdmin(id: string, request: CipherRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/admin", request, true, true); + return new CipherResponse(r); + } + + deleteCipher(id: string): Promise { + return this.send("DELETE", "/ciphers/" + id, null, true, false); + } + + deleteCipherAdmin(id: string): Promise { + return this.send("DELETE", "/ciphers/" + id + "/admin", null, true, false); + } + + deleteManyCiphers(request: CipherBulkDeleteRequest): Promise { + return this.send("DELETE", "/ciphers", request, true, false); + } + + deleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise { + return this.send("DELETE", "/ciphers/admin", request, true, false); + } + + putMoveCiphers(request: CipherBulkMoveRequest): Promise { + return this.send("PUT", "/ciphers/move", request, true, false); + } + + async putShareCipher(id: string, request: CipherShareRequest): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/share", request, true, true); + return new CipherResponse(r); + } + + putShareCiphers(request: CipherBulkShareRequest): Promise { + return this.send("PUT", "/ciphers/share", request, true, false); + } + + putCipherCollections(id: string, request: CipherCollectionsRequest): Promise { + return this.send("PUT", "/ciphers/" + id + "/collections", request, true, false); + } + + putCipherCollectionsAdmin(id: string, request: CipherCollectionsRequest): Promise { + return this.send("PUT", "/ciphers/" + id + "/collections-admin", request, true, false); + } + + postPurgeCiphers( + request: SecretVerificationRequest, + organizationId: string = null + ): Promise { + let path = "/ciphers/purge"; + if (organizationId != null) { + path += "?organizationId=" + organizationId; + } + return this.send("POST", path, request, true, false); + } + + postImportCiphers(request: ImportCiphersRequest): Promise { + return this.send("POST", "/ciphers/import", request, true, false); + } + + postImportOrganizationCiphers( + organizationId: string, + request: ImportOrganizationCiphersRequest + ): Promise { + return this.send( + "POST", + "/ciphers/import-organization?organizationId=" + organizationId, + request, + true, + false + ); + } + + putDeleteCipher(id: string): Promise { + return this.send("PUT", "/ciphers/" + id + "/delete", null, true, false); + } + + putDeleteCipherAdmin(id: string): Promise { + return this.send("PUT", "/ciphers/" + id + "/delete-admin", null, true, false); + } + + putDeleteManyCiphers(request: CipherBulkDeleteRequest): Promise { + return this.send("PUT", "/ciphers/delete", request, true, false); + } + + putDeleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise { + return this.send("PUT", "/ciphers/delete-admin", request, true, false); + } + + async putRestoreCipher(id: string): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/restore", null, true, true); + return new CipherResponse(r); + } + + async putRestoreCipherAdmin(id: string): Promise { + const r = await this.send("PUT", "/ciphers/" + id + "/restore-admin", null, true, true); + return new CipherResponse(r); + } + + async putRestoreManyCiphers( + request: CipherBulkDeleteRequest + ): Promise> { + const r = await this.send("PUT", "/ciphers/restore", request, true, true); + return new ListResponse(r, CipherResponse); + } + + // Attachments APIs + + async getAttachmentData( + cipherId: string, + attachmentId: string, + emergencyAccessId?: string + ): Promise { + const path = + (emergencyAccessId != null ? "/emergency-access/" + emergencyAccessId + "/" : "/ciphers/") + + cipherId + + "/attachment/" + + attachmentId; + const r = await this.send("GET", path, null, true, true); + return new AttachmentResponse(r); + } + + async postCipherAttachment( + id: string, + request: AttachmentRequest + ): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment/v2", request, true, true); + return new AttachmentUploadDataResponse(r); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postCipherAttachmentLegacy(id: string, data: FormData): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment", data, true, true); + return new CipherResponse(r); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async postCipherAttachmentAdminLegacy(id: string, data: FormData): Promise { + const r = await this.send("POST", "/ciphers/" + id + "/attachment-admin", data, true, true); + return new CipherResponse(r); + } + + deleteCipherAttachment(id: string, attachmentId: string): Promise { + return this.send("DELETE", "/ciphers/" + id + "/attachment/" + attachmentId, null, true, false); + } + + deleteCipherAttachmentAdmin(id: string, attachmentId: string): Promise { + return this.send( + "DELETE", + "/ciphers/" + id + "/attachment/" + attachmentId + "/admin", + null, + true, + false + ); + } + + postShareCipherAttachment( + id: string, + attachmentId: string, + data: FormData, + organizationId: string + ): Promise { + return this.send( + "POST", + "/ciphers/" + id + "/attachment/" + attachmentId + "/share?organizationId=" + organizationId, + data, + true, + false + ); + } + + async renewAttachmentUploadUrl( + id: string, + attachmentId: string + ): Promise { + const r = await this.send( + "GET", + "/ciphers/" + id + "/attachment/" + attachmentId + "/renew", + null, + true, + true + ); + return new AttachmentUploadDataResponse(r); + } + + postAttachmentFile(id: string, attachmentId: string, data: FormData): Promise { + return this.send("POST", "/ciphers/" + id + "/attachment/" + attachmentId, data, true, false); + } + + // Collections APIs + + async getCollectionDetails( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections/" + id + "/details", + null, + true, + true + ); + return new CollectionGroupDetailsResponse(r); + } + + async getUserCollections(): Promise> { + const r = await this.send("GET", "/collections", null, true, true); + return new ListResponse(r, CollectionResponse); + } + + async getCollections(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections", + null, + true, + true + ); + return new ListResponse(r, CollectionResponse); + } + + async getCollectionUsers( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/collections/" + id + "/users", + null, + true, + true + ); + return r.map((dr: any) => new SelectionReadOnlyResponse(dr)); + } + + async postCollection( + organizationId: string, + request: CollectionRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/collections", + request, + true, + true + ); + return new CollectionResponse(r); + } + + async putCollection( + organizationId: string, + id: string, + request: CollectionRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/collections/" + id, + request, + true, + true + ); + return new CollectionResponse(r); + } + + async putCollectionUsers( + organizationId: string, + id: string, + request: SelectionReadOnlyRequest[] + ): Promise { + await this.send( + "PUT", + "/organizations/" + organizationId + "/collections/" + id + "/users", + request, + true, + false + ); + } + + deleteCollection(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/collections/" + id, + null, + true, + false + ); + } + + deleteCollectionUser( + organizationId: string, + id: string, + organizationUserId: string + ): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/collections/" + id + "/user/" + organizationUserId, + null, + true, + false + ); + } + + // Groups APIs + + async getGroupDetails(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups/" + id + "/details", + null, + true, + true + ); + return new GroupDetailsResponse(r); + } + + async getGroups(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups", + null, + true, + true + ); + return new ListResponse(r, GroupResponse); + } + + async getGroupUsers(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/groups/" + id + "/users", + null, + true, + true + ); + return r; + } + + async postGroup(organizationId: string, request: GroupRequest): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/groups", + request, + true, + true + ); + return new GroupResponse(r); + } + + async putGroup( + organizationId: string, + id: string, + request: GroupRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/groups/" + id, + request, + true, + true + ); + return new GroupResponse(r); + } + + async putGroupUsers(organizationId: string, id: string, request: string[]): Promise { + await this.send( + "PUT", + "/organizations/" + organizationId + "/groups/" + id + "/users", + request, + true, + false + ); + } + + deleteGroup(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/groups/" + id, + null, + true, + false + ); + } + + deleteGroupUser(organizationId: string, id: string, organizationUserId: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/groups/" + id + "/user/" + organizationUserId, + null, + true, + false + ); + } + + // Policy APIs + + async getPolicy(organizationId: string, type: PolicyType): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies/" + type, + null, + true, + true + ); + return new PolicyResponse(r); + } + + async getPolicies(organizationId: string): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies", + null, + true, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async getPoliciesByToken( + organizationId: string, + token: string, + email: string, + organizationUserId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + + organizationId + + "/policies/token?" + + "token=" + + encodeURIComponent(token) + + "&email=" + + encodeURIComponent(email) + + "&organizationUserId=" + + organizationUserId, + null, + false, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async getPoliciesByInvitedUser( + organizationId: string, + userId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/policies/invited-user?" + "userId=" + userId, + null, + false, + true + ); + return new ListResponse(r, PolicyResponse); + } + + async putPolicy( + organizationId: string, + type: PolicyType, + request: PolicyRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/policies/" + type, + request, + true, + true + ); + return new PolicyResponse(r); + } + + // Organization User APIs + + async getOrganizationUser( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id, + null, + true, + true + ); + return new OrganizationUserDetailsResponse(r); + } + + async getOrganizationUserGroups(organizationId: string, id: string): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id + "/groups", + null, + true, + true + ); + return r; + } + + async getOrganizationUsers( + organizationId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users", + null, + true, + true + ); + return new ListResponse(r, OrganizationUserUserDetailsResponse); + } + + async getOrganizationUserResetPasswordDetails( + organizationId: string, + id: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/users/" + id + "/reset-password-details", + null, + true, + true + ); + return new OrganizationUserResetPasswordDetailsReponse(r); + } + + async getOrganizationAutoEnrollStatus( + identifier: string + ): Promise { + const r = await this.send( + "GET", + "/organizations/" + identifier + "/auto-enroll-status", + null, + true, + true + ); + return new OrganizationAutoEnrollStatusResponse(r); + } + + postOrganizationUserInvite( + organizationId: string, + request: OrganizationUserInviteRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/invite", + request, + true, + false + ); + } + + postOrganizationUserReinvite(organizationId: string, id: string): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/reinvite", + null, + true, + false + ); + } + + async postManyOrganizationUserReinvite( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/reinvite", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + postOrganizationUserAccept( + organizationId: string, + id: string, + request: OrganizationUserAcceptRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/accept", + request, + true, + false + ); + } + + postOrganizationUserConfirm( + organizationId: string, + id: string, + request: OrganizationUserConfirmRequest + ): Promise { + return this.send( + "POST", + "/organizations/" + organizationId + "/users/" + id + "/confirm", + request, + true, + false + ); + } + + async postOrganizationUsersPublicKey( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/public-keys", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkPublicKeyResponse); + } + + async postOrganizationUserBulkConfirm( + organizationId: string, + request: OrganizationUserBulkConfirmRequest + ): Promise> { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/users/confirm", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + putOrganizationUser( + organizationId: string, + id: string, + request: OrganizationUserUpdateRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id, + request, + true, + false + ); + } + + putOrganizationUserGroups( + organizationId: string, + id: string, + request: OrganizationUserUpdateGroupsRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id + "/groups", + request, + true, + false + ); + } + + putOrganizationUserResetPasswordEnrollment( + organizationId: string, + userId: string, + request: OrganizationUserResetPasswordEnrollmentRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + userId + "/reset-password-enrollment", + request, + true, + false + ); + } + + putOrganizationUserResetPassword( + organizationId: string, + id: string, + request: OrganizationUserResetPasswordRequest + ): Promise { + return this.send( + "PUT", + "/organizations/" + organizationId + "/users/" + id + "/reset-password", + request, + true, + false + ); + } + + deleteOrganizationUser(organizationId: string, id: string): Promise { + return this.send( + "DELETE", + "/organizations/" + organizationId + "/users/" + id, + null, + true, + false + ); + } + + async deleteManyOrganizationUsers( + organizationId: string, + request: OrganizationUserBulkRequest + ): Promise> { + const r = await this.send( + "DELETE", + "/organizations/" + organizationId + "/users", + request, + true, + true + ); + return new ListResponse(r, OrganizationUserBulkResponse); + } + + // Plan APIs + + async getPlans(): Promise> { + const r = await this.send("GET", "/plans/", null, true, true); + return new ListResponse(r, PlanResponse); + } + + async postImportDirectory(organizationId: string, request: ImportDirectoryRequest): Promise { + return this.send("POST", "/organizations/" + organizationId + "/import", request, true, false); + } + + async postPublicImportDirectory(request: OrganizationImportRequest): Promise { + return this.send("POST", "/public/organization/import", request, true, false); + } + + async getTaxRates(): Promise> { + const r = await this.send("GET", "/plans/sales-tax-rates/", null, true, true); + return new ListResponse(r, TaxRateResponse); + } + + // Settings APIs + + async getSettingsDomains(): Promise { + const r = await this.send("GET", "/settings/domains", null, true, true); + return new DomainsResponse(r); + } + + async putSettingsDomains(request: UpdateDomainsRequest): Promise { + const r = await this.send("PUT", "/settings/domains", request, true, true); + return new DomainsResponse(r); + } + + // Sync APIs + + async getSync(): Promise { + const path = this.isDesktopClient || this.isWebClient ? "/sync?excludeDomains=true" : "/sync"; + const r = await this.send("GET", path, null, true, true); + return new SyncResponse(r); + } + + // Two-factor APIs + + async getTwoFactorProviders(): Promise> { + const r = await this.send("GET", "/two-factor", null, true, true); + return new ListResponse(r, TwoFactorProviderResponse); + } + + async getTwoFactorOrganizationProviders( + organizationId: string + ): Promise> { + const r = await this.send( + "GET", + "/organizations/" + organizationId + "/two-factor", + null, + true, + true + ); + return new ListResponse(r, TwoFactorProviderResponse); + } + + async getTwoFactorAuthenticator( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-authenticator", request, true, true); + return new TwoFactorAuthenticatorResponse(r); + } + + async getTwoFactorEmail(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-email", request, true, true); + return new TwoFactorEmailResponse(r); + } + + async getTwoFactorDuo(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-duo", request, true, true); + return new TwoFactorDuoResponse(r); + } + + async getTwoFactorOrganizationDuo( + organizationId: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + organizationId + "/two-factor/get-duo", + request, + true, + true + ); + return new TwoFactorDuoResponse(r); + } + + async getTwoFactorYubiKey(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-yubikey", request, true, true); + return new TwoFactorYubiKeyResponse(r); + } + + async getTwoFactorWebAuthn( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-webauthn", request, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async getTwoFactorWebAuthnChallenge( + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/two-factor/get-webauthn-challenge", request, true, true); + return new ChallengeResponse(r); + } + + async getTwoFactorRecover(request: SecretVerificationRequest): Promise { + const r = await this.send("POST", "/two-factor/get-recover", request, true, true); + return new TwoFactorRecoverResponse(r); + } + + async putTwoFactorAuthenticator( + request: UpdateTwoFactorAuthenticatorRequest + ): Promise { + const r = await this.send("PUT", "/two-factor/authenticator", request, true, true); + return new TwoFactorAuthenticatorResponse(r); + } + + async putTwoFactorEmail(request: UpdateTwoFactorEmailRequest): Promise { + const r = await this.send("PUT", "/two-factor/email", request, true, true); + return new TwoFactorEmailResponse(r); + } + + async putTwoFactorDuo(request: UpdateTwoFactorDuoRequest): Promise { + const r = await this.send("PUT", "/two-factor/duo", request, true, true); + return new TwoFactorDuoResponse(r); + } + + async putTwoFactorOrganizationDuo( + organizationId: string, + request: UpdateTwoFactorDuoRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/two-factor/duo", + request, + true, + true + ); + return new TwoFactorDuoResponse(r); + } + + async putTwoFactorYubiKey( + request: UpdateTwoFactorYubioOtpRequest + ): Promise { + const r = await this.send("PUT", "/two-factor/yubikey", request, true, true); + return new TwoFactorYubiKeyResponse(r); + } + + async putTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnRequest + ): Promise { + const response = request.deviceResponse.response as AuthenticatorAttestationResponse; + const data: any = Object.assign({}, request); + + data.deviceResponse = { + id: request.deviceResponse.id, + rawId: btoa(request.deviceResponse.id), + type: request.deviceResponse.type, + extensions: request.deviceResponse.getClientExtensionResults(), + response: { + AttestationObject: Utils.fromBufferToB64(response.attestationObject), + clientDataJson: Utils.fromBufferToB64(response.clientDataJSON), + }, + }; + + const r = await this.send("PUT", "/two-factor/webauthn", data, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async deleteTwoFactorWebAuthn( + request: UpdateTwoFactorWebAuthnDeleteRequest + ): Promise { + const r = await this.send("DELETE", "/two-factor/webauthn", request, true, true); + return new TwoFactorWebAuthnResponse(r); + } + + async putTwoFactorDisable(request: TwoFactorProviderRequest): Promise { + const r = await this.send("PUT", "/two-factor/disable", request, true, true); + return new TwoFactorProviderResponse(r); + } + + async putTwoFactorOrganizationDisable( + organizationId: string, + request: TwoFactorProviderRequest + ): Promise { + const r = await this.send( + "PUT", + "/organizations/" + organizationId + "/two-factor/disable", + request, + true, + true + ); + return new TwoFactorProviderResponse(r); + } + + postTwoFactorRecover(request: TwoFactorRecoveryRequest): Promise { + return this.send("POST", "/two-factor/recover", request, false, false); + } + + postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise { + return this.send("POST", "/two-factor/send-email", request, true, false); + } + + postTwoFactorEmail(request: TwoFactorEmailRequest): Promise { + return this.send("POST", "/two-factor/send-email-login", request, false, false); + } + + // Emergency Access APIs + + async getEmergencyAccessTrusted(): Promise> { + const r = await this.send("GET", "/emergency-access/trusted", null, true, true); + return new ListResponse(r, EmergencyAccessGranteeDetailsResponse); + } + + async getEmergencyAccessGranted(): Promise> { + const r = await this.send("GET", "/emergency-access/granted", null, true, true); + return new ListResponse(r, EmergencyAccessGrantorDetailsResponse); + } + + async getEmergencyAccess(id: string): Promise { + const r = await this.send("GET", "/emergency-access/" + id, null, true, true); + return new EmergencyAccessGranteeDetailsResponse(r); + } + + async getEmergencyGrantorPolicies(id: string): Promise> { + const r = await this.send("GET", "/emergency-access/" + id + "/policies", null, true, true); + return new ListResponse(r, PolicyResponse); + } + + putEmergencyAccess(id: string, request: EmergencyAccessUpdateRequest): Promise { + return this.send("PUT", "/emergency-access/" + id, request, true, false); + } + + deleteEmergencyAccess(id: string): Promise { + return this.send("DELETE", "/emergency-access/" + id, null, true, false); + } + + postEmergencyAccessInvite(request: EmergencyAccessInviteRequest): Promise { + return this.send("POST", "/emergency-access/invite", request, true, false); + } + + postEmergencyAccessReinvite(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/reinvite", null, true, false); + } + + postEmergencyAccessAccept(id: string, request: EmergencyAccessAcceptRequest): Promise { + return this.send("POST", "/emergency-access/" + id + "/accept", request, true, false); + } + + postEmergencyAccessConfirm(id: string, request: EmergencyAccessConfirmRequest): Promise { + return this.send("POST", "/emergency-access/" + id + "/confirm", request, true, false); + } + + postEmergencyAccessInitiate(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/initiate", null, true, false); + } + + postEmergencyAccessApprove(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/approve", null, true, false); + } + + postEmergencyAccessReject(id: string): Promise { + return this.send("POST", "/emergency-access/" + id + "/reject", null, true, false); + } + + async postEmergencyAccessTakeover(id: string): Promise { + const r = await this.send("POST", "/emergency-access/" + id + "/takeover", null, true, true); + return new EmergencyAccessTakeoverResponse(r); + } + + async postEmergencyAccessPassword( + id: string, + request: EmergencyAccessPasswordRequest + ): Promise { + await this.send("POST", "/emergency-access/" + id + "/password", request, true, true); + } + + async postEmergencyAccessView(id: string): Promise { + const r = await this.send("POST", "/emergency-access/" + id + "/view", null, true, true); + return new EmergencyAccessViewResponse(r); + } + + // Organization APIs + + async getOrganization(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id, null, true, true); + return new OrganizationResponse(r); + } + + async getOrganizationBilling(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/billing", null, true, true); + return new BillingResponse(r); + } + + async getOrganizationSubscription(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/subscription", null, true, true); + return new OrganizationSubscriptionResponse(r); + } + + async getOrganizationLicense(id: string, installationId: string): Promise { + return this.send( + "GET", + "/organizations/" + id + "/license?installationId=" + installationId, + null, + true, + true + ); + } + + async getOrganizationTaxInfo(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/tax", null, true, true); + return new TaxInfoResponse(r); + } + + async getOrganizationSso(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/sso", null, true, true); + return new OrganizationSsoResponse(r); + } + + async postOrganization(request: OrganizationCreateRequest): Promise { + const r = await this.send("POST", "/organizations", request, true, true); + return new OrganizationResponse(r); + } + + async putOrganization( + id: string, + request: OrganizationUpdateRequest + ): Promise { + const r = await this.send("PUT", "/organizations/" + id, request, true, true); + return new OrganizationResponse(r); + } + + async putOrganizationTaxInfo( + id: string, + request: OrganizationTaxInfoUpdateRequest + ): Promise { + return this.send("PUT", "/organizations/" + id + "/tax", request, true, false); + } + + postLeaveOrganization(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/leave", null, true, false); + } + + async postOrganizationLicense(data: FormData): Promise { + const r = await this.send("POST", "/organizations/license", data, true, true); + return new OrganizationResponse(r); + } + + async postOrganizationLicenseUpdate(id: string, data: FormData): Promise { + return this.send("POST", "/organizations/" + id + "/license", data, true, false); + } + + async postOrganizationApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/api-key", request, true, true); + return new ApiKeyResponse(r); + } + + async postOrganizationRotateApiKey( + id: string, + request: SecretVerificationRequest + ): Promise { + const r = await this.send( + "POST", + "/organizations/" + id + "/rotate-api-key", + request, + true, + true + ); + return new ApiKeyResponse(r); + } + + async postOrganizationSso( + id: string, + request: OrganizationSsoRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/sso", request, true, true); + return new OrganizationSsoResponse(r); + } + + async postOrganizationUpgrade( + id: string, + request: OrganizationUpgradeRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/upgrade", request, true, true); + return new PaymentResponse(r); + } + + async postOrganizationUpdateSubscription( + id: string, + request: OrganizationSubscriptionUpdateRequest + ): Promise { + return this.send("POST", "/organizations/" + id + "/subscription", request, true, false); + } + + async postOrganizationSeat(id: string, request: SeatRequest): Promise { + const r = await this.send("POST", "/organizations/" + id + "/seat", request, true, true); + return new PaymentResponse(r); + } + + async postOrganizationStorage(id: string, request: StorageRequest): Promise { + const r = await this.send("POST", "/organizations/" + id + "/storage", request, true, true); + return new PaymentResponse(r); + } + + postOrganizationPayment(id: string, request: PaymentRequest): Promise { + return this.send("POST", "/organizations/" + id + "/payment", request, true, false); + } + + postOrganizationVerifyBank(id: string, request: VerifyBankRequest): Promise { + return this.send("POST", "/organizations/" + id + "/verify-bank", request, true, false); + } + + postOrganizationCancel(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/cancel", null, true, false); + } + + postOrganizationReinstate(id: string): Promise { + return this.send("POST", "/organizations/" + id + "/reinstate", null, true, false); + } + + deleteOrganization(id: string, request: SecretVerificationRequest): Promise { + return this.send("DELETE", "/organizations/" + id, request, true, false); + } + + async getOrganizationKeys(id: string): Promise { + const r = await this.send("GET", "/organizations/" + id + "/keys", null, true, true); + return new OrganizationKeysResponse(r); + } + + async postOrganizationKeys( + id: string, + request: OrganizationKeysRequest + ): Promise { + const r = await this.send("POST", "/organizations/" + id + "/keys", request, true, true); + return new OrganizationKeysResponse(r); + } + + // Provider APIs + + async postProviderSetup(id: string, request: ProviderSetupRequest) { + const r = await this.send("POST", "/providers/" + id + "/setup", request, true, true); + return new ProviderResponse(r); + } + + async getProvider(id: string) { + const r = await this.send("GET", "/providers/" + id, null, true, true); + return new ProviderResponse(r); + } + + async putProvider(id: string, request: ProviderUpdateRequest) { + const r = await this.send("PUT", "/providers/" + id, request, true, true); + return new ProviderResponse(r); + } + + // Provider User APIs + + async getProviderUsers( + providerId: string + ): Promise> { + const r = await this.send("GET", "/providers/" + providerId + "/users", null, true, true); + return new ListResponse(r, ProviderUserUserDetailsResponse); + } + + async getProviderUser(providerId: string, id: string): Promise { + const r = await this.send("GET", "/providers/" + providerId + "/users/" + id, null, true, true); + return new ProviderUserResponse(r); + } + + postProviderUserInvite(providerId: string, request: ProviderUserInviteRequest): Promise { + return this.send("POST", "/providers/" + providerId + "/users/invite", request, true, false); + } + + postProviderUserReinvite(providerId: string, id: string): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/reinvite", + null, + true, + false + ); + } + + async postManyProviderUserReinvite( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/reinvite", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkResponse); + } + + async postProviderUserBulkConfirm( + providerId: string, + request: ProviderUserBulkConfirmRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/confirm", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkResponse); + } + + async deleteManyProviderUsers( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send("DELETE", "/providers/" + providerId + "/users", request, true, true); + return new ListResponse(r, ProviderUserBulkResponse); + } + + postProviderUserAccept( + providerId: string, + id: string, + request: ProviderUserAcceptRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/accept", + request, + true, + false + ); + } + + postProviderUserConfirm( + providerId: string, + id: string, + request: ProviderUserConfirmRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/users/" + id + "/confirm", + request, + true, + false + ); + } + + async postProviderUsersPublicKey( + providerId: string, + request: ProviderUserBulkRequest + ): Promise> { + const r = await this.send( + "POST", + "/providers/" + providerId + "/users/public-keys", + request, + true, + true + ); + return new ListResponse(r, ProviderUserBulkPublicKeyResponse); + } + + putProviderUser( + providerId: string, + id: string, + request: ProviderUserUpdateRequest + ): Promise { + return this.send("PUT", "/providers/" + providerId + "/users/" + id, request, true, false); + } + + deleteProviderUser(providerId: string, id: string): Promise { + return this.send("DELETE", "/providers/" + providerId + "/users/" + id, null, true, false); + } + + // Provider Organization APIs + + async getProviderClients( + providerId: string + ): Promise> { + const r = await this.send( + "GET", + "/providers/" + providerId + "/organizations", + null, + true, + true + ); + return new ListResponse(r, ProviderOrganizationOrganizationDetailsResponse); + } + + postProviderAddOrganization( + providerId: string, + request: ProviderAddOrganizationRequest + ): Promise { + return this.send( + "POST", + "/providers/" + providerId + "/organizations/add", + request, + true, + false + ); + } + + async postProviderCreateOrganization( + providerId: string, + request: ProviderOrganizationCreateRequest + ): Promise { + const r = await this.send( + "POST", + "/providers/" + providerId + "/organizations", + request, + true, + true + ); + return new ProviderOrganizationResponse(r); + } + + deleteProviderOrganization(providerId: string, id: string): Promise { + return this.send( + "DELETE", + "/providers/" + providerId + "/organizations/" + id, + null, + true, + false + ); + } + + // Event APIs + + async getEvents(start: string, end: string, token: string): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsCipher( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/ciphers/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsOrganization( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/organizations/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsOrganizationUser( + organizationId: string, + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters( + "/organizations/" + organizationId + "/users/" + id + "/events", + start, + end, + token + ), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsProvider( + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters("/providers/" + id + "/events", start, end, token), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async getEventsProviderUser( + providerId: string, + id: string, + start: string, + end: string, + token: string + ): Promise> { + const r = await this.send( + "GET", + this.addEventParameters( + "/providers/" + providerId + "/users/" + id + "/events", + start, + end, + token + ), + null, + true, + true + ); + return new ListResponse(r, EventResponse); + } + + async postEventsCollect(request: EventRequest[]): Promise { + const authHeader = await this.getActiveBearerToken(); + const headers = new Headers({ + "Device-Type": this.deviceType, + Authorization: "Bearer " + authHeader, + "Content-Type": "application/json; charset=utf-8", + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + const response = await this.fetch( + new Request(this.environmentService.getEventsUrl() + "/collect", { + cache: "no-store", + credentials: this.getCredentials(), + method: "POST", + body: JSON.stringify(request), + headers: headers, + }) + ); + if (response.status !== 200) { + return Promise.reject("Event post failed."); + } + } + + // User APIs + + async getUserPublicKey(id: string): Promise { + const r = await this.send("GET", "/users/" + id + "/public-key", null, true, true); + return new UserKeyResponse(r); + } + + // HIBP APIs + + async getHibpBreach(username: string): Promise { + const r = await this.send("GET", "/hibp/breach?username=" + username, null, true, true); + return r.map((a: any) => new BreachAccountResponse(a)); + } + + // Misc + + async postBitPayInvoice(request: BitPayInvoiceRequest): Promise { + const r = await this.send("POST", "/bitpay-invoice", request, true, true); + return r as string; + } + + async postSetupPayment(): Promise { + const r = await this.send("POST", "/setup-payment", null, true, true); + return r as string; + } + + // Key Connector + + async getUserKeyFromKeyConnector(keyConnectorUrl: string): Promise { + const authHeader = await this.getActiveBearerToken(); + + const response = await this.fetch( + new Request(keyConnectorUrl + "/user-keys", { + cache: "no-store", + method: "GET", + headers: new Headers({ + Accept: "application/json", + Authorization: "Bearer " + authHeader, + }), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + + return new KeyConnectorUserKeyResponse(await response.json()); + } + + async postUserKeyToKeyConnector( + keyConnectorUrl: string, + request: KeyConnectorUserKeyRequest + ): Promise { + const authHeader = await this.getActiveBearerToken(); + + const response = await this.fetch( + new Request(keyConnectorUrl + "/user-keys", { + cache: "no-store", + method: "POST", + headers: new Headers({ + Accept: "application/json", + Authorization: "Bearer " + authHeader, + "Content-Type": "application/json; charset=utf-8", + }), + body: JSON.stringify(request), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + async getKeyConnectorAlive(keyConnectorUrl: string) { + const response = await this.fetch( + new Request(keyConnectorUrl + "/alive", { + cache: "no-store", + method: "GET", + headers: new Headers({ + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }), + }) + ); + + if (response.status !== 200) { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + // Helpers + + async getActiveBearerToken(): Promise { + let accessToken = await this.tokenService.getToken(); + if (await this.tokenService.tokenNeedsRefresh()) { + await this.doAuthRefresh(); + accessToken = await this.tokenService.getToken(); + } + return accessToken; + } + + async fetch(request: Request): Promise { + if (request.method === "GET") { + request.headers.set("Cache-Control", "no-store"); + request.headers.set("Pragma", "no-cache"); + } + request.headers.set("Bitwarden-Client-Name", this.platformUtilsService.getClientType()); + request.headers.set( + "Bitwarden-Client-Version", + await this.platformUtilsService.getApplicationVersion() + ); + return this.nativeFetch(request); + } + + nativeFetch(request: Request): Promise { + return fetch(request); + } + + async preValidateSso(identifier: string): Promise { + if (identifier == null || identifier === "") { + throw new Error("Organization Identifier was not provided."); + } + const headers = new Headers({ + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const path = `/account/prevalidate?domainHint=${encodeURIComponent(identifier)}`; + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + path, { + cache: "no-store", + credentials: this.getCredentials(), + headers: headers, + method: "GET", + }) + ); + + if (response.status === 200) { + return true; + } else { + const error = await this.handleError(response, false, true); + return Promise.reject(error); + } + } + + async postCreateSponsorship( + sponsoredOrgId: string, + request: OrganizationSponsorshipCreateRequest + ): Promise { + return await this.send( + "POST", + "/organization/sponsorship/" + sponsoredOrgId + "/families-for-enterprise", + request, + true, + false + ); + } + + async deleteRevokeSponsorship(sponsoringOrganizationId: string): Promise { + return await this.send( + "DELETE", + "/organization/sponsorship/" + sponsoringOrganizationId, + null, + true, + false + ); + } + + async deleteRemoveSponsorship(sponsoringOrgId: string): Promise { + return await this.send( + "DELETE", + "/organization/sponsorship/sponsored/" + sponsoringOrgId, + null, + true, + false + ); + } + + async postPreValidateSponsorshipToken(sponsorshipToken: string): Promise { + const r = await this.send( + "POST", + "/organization/sponsorship/validate-token?sponsorshipToken=" + + encodeURIComponent(sponsorshipToken), + null, + true, + true + ); + return r as boolean; + } + + async postRedeemSponsorship( + sponsorshipToken: string, + request: OrganizationSponsorshipRedeemRequest + ): Promise { + return await this.send( + "POST", + "/organization/sponsorship/redeem?sponsorshipToken=" + encodeURIComponent(sponsorshipToken), + request, + true, + false + ); + } + + async postResendSponsorshipOffer(sponsoringOrgId: string): Promise { + return await this.send( + "POST", + "/organization/sponsorship/" + sponsoringOrgId + "/families-for-enterprise/resend", + null, + true, + false + ); + } + + protected async doAuthRefresh(): Promise { + const refreshToken = await this.tokenService.getRefreshToken(); + if (refreshToken != null && refreshToken !== "") { + return this.doRefreshToken(); + } + + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + if (!Utils.isNullOrWhitespace(clientId) && !Utils.isNullOrWhitespace(clientSecret)) { + return this.doApiTokenRefresh(); + } + + throw new Error("Cannot refresh token, no refresh token or api keys are stored"); + } + + protected async doRefreshToken(): Promise { + const refreshToken = await this.tokenService.getRefreshToken(); + if (refreshToken == null || refreshToken === "") { + throw new Error(); + } + const headers = new Headers({ + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + Accept: "application/json", + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const decodedToken = await this.tokenService.decodeToken(); + const response = await this.fetch( + new Request(this.environmentService.getIdentityUrl() + "/connect/token", { + body: this.qsStringify({ + grant_type: "refresh_token", + client_id: decodedToken.client_id, + refresh_token: refreshToken, + }), + cache: "no-store", + credentials: this.getCredentials(), + headers: headers, + method: "POST", + }) + ); + + if (response.status === 200) { + const responseJson = await response.json(); + const tokenResponse = new IdentityTokenResponse(responseJson); + await this.tokenService.setTokens( + tokenResponse.accessToken, + tokenResponse.refreshToken, + null + ); + } else { + const error = await this.handleError(response, true, true); + return Promise.reject(error); + } + } + + protected async doApiTokenRefresh(): Promise { + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + + const appId = await this.appIdService.getAppId(); + const deviceRequest = new DeviceRequest(appId, this.platformUtilsService); + + const tokenRequest = new ApiTokenRequest( + clientId, + clientSecret, + new TokenRequestTwoFactor(), + deviceRequest + ); + + const response = await this.postIdentityToken(tokenRequest); + if (!(response instanceof IdentityTokenResponse)) { + throw new Error("Invalid response received when refreshing api token"); + } + + await this.tokenService.setToken(response.accessToken); + } + + private async send( + method: "GET" | "POST" | "PUT" | "DELETE", + path: string, + body: any, + authed: boolean, + hasResponse: boolean, + apiUrl?: string, + alterHeaders?: (headers: Headers) => void + ): Promise { + apiUrl = Utils.isNullOrWhitespace(apiUrl) ? this.environmentService.getApiUrl() : apiUrl; + + const requestUrl = apiUrl + path; + // Prevent directory traversal from malicious paths + if (new URL(requestUrl).href !== requestUrl) { + return Promise.reject("Invalid request url path."); + } + + const headers = new Headers({ + "Device-Type": this.deviceType, + }); + if (this.customUserAgent != null) { + headers.set("User-Agent", this.customUserAgent); + } + + const requestInit: RequestInit = { + cache: "no-store", + credentials: this.getCredentials(), + method: method, + }; + + if (authed) { + const authHeader = await this.getActiveBearerToken(); + headers.set("Authorization", "Bearer " + authHeader); + } + if (body != null) { + if (typeof body === "string") { + requestInit.body = body; + headers.set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); + } else if (typeof body === "object") { + if (body instanceof FormData) { + requestInit.body = body; + } else { + headers.set("Content-Type", "application/json; charset=utf-8"); + requestInit.body = JSON.stringify(body); + } + } + } + if (hasResponse) { + headers.set("Accept", "application/json"); + } + if (alterHeaders != null) { + alterHeaders(headers); + } + + requestInit.headers = headers; + const response = await this.fetch(new Request(requestUrl, requestInit)); + + if (hasResponse && response.status === 200) { + const responseJson = await response.json(); + return responseJson; + } else if (response.status !== 200) { + const error = await this.handleError(response, false, authed); + return Promise.reject(error); + } + } + + private async handleError( + response: Response, + tokenError: boolean, + authed: boolean + ): Promise { + if ( + authed && + ((tokenError && response.status === 400) || + response.status === 401 || + response.status === 403) + ) { + await this.logoutCallback(true); + return null; + } + + let responseJson: any = null; + if (this.isJsonResponse(response)) { + responseJson = await response.json(); + } else if (this.isTextResponse(response)) { + responseJson = { Message: await response.text() }; + } + + return new ErrorResponse(responseJson, response.status, tokenError); + } + + private qsStringify(params: any): string { + return Object.keys(params) + .map((key) => { + return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]); + }) + .join("&"); + } + + private getCredentials(): RequestCredentials { + if (!this.isWebClient || this.environmentService.hasBaseUrl()) { + return "include"; + } + return undefined; + } + + private addEventParameters(base: string, start: string, end: string, token: string) { + if (start != null) { + base += "?start=" + start; + } + if (end != null) { + base += base.indexOf("?") > -1 ? "&" : "?"; + base += "end=" + end; + } + if (token != null) { + base += base.indexOf("?") > -1 ? "&" : "?"; + base += "continuationToken=" + token; + } + return base; + } + + private isJsonResponse(response: Response): boolean { + const typeHeader = response.headers.get("content-type"); + return typeHeader != null && typeHeader.indexOf("application/json") > -1; + } + + private isTextResponse(response: Response): boolean { + const typeHeader = response.headers.get("content-type"); + return typeHeader != null && typeHeader.indexOf("text") > -1; + } +} diff --git a/jslib/common/src/services/appId.service.ts b/jslib/common/src/services/appId.service.ts new file mode 100644 index 00000000..a6406fdc --- /dev/null +++ b/jslib/common/src/services/appId.service.ts @@ -0,0 +1,31 @@ +import { AppIdService as AppIdServiceAbstraction } from "../abstractions/appId.service"; +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { Utils } from "../misc/utils"; + +export class AppIdService implements AppIdServiceAbstraction { + constructor(private storageService: StorageService) {} + + getAppId(): Promise { + return this.makeAndGetAppId("appId"); + } + + getAnonymousAppId(): Promise { + return this.makeAndGetAppId("anonymousAppId"); + } + + private async makeAndGetAppId(key: string) { + const existingId = await this.storageService.get(key, { + htmlStorageLocation: HtmlStorageLocation.Local, + }); + if (existingId != null) { + return existingId; + } + + const guid = Utils.newGuid(); + await this.storageService.save(key, guid, { + htmlStorageLocation: HtmlStorageLocation.Local, + }); + return guid; + } +} diff --git a/jslib/common/src/services/audit.service.ts b/jslib/common/src/services/audit.service.ts new file mode 100644 index 00000000..4ceebf67 --- /dev/null +++ b/jslib/common/src/services/audit.service.ts @@ -0,0 +1,44 @@ +import { ApiService } from "../abstractions/api.service"; +import { AuditService as AuditServiceAbstraction } from "../abstractions/audit.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { throttle } from "../misc/throttle"; +import { Utils } from "../misc/utils"; +import { BreachAccountResponse } from "../models/response/breachAccountResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; + +const PwnedPasswordsApi = "https://api.pwnedpasswords.com/range/"; + +export class AuditService implements AuditServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private apiService: ApiService + ) {} + + @throttle(100, () => "passwordLeaked") + async passwordLeaked(password: string): Promise { + const hashBytes = await this.cryptoFunctionService.hash(password, "sha1"); + const hash = Utils.fromBufferToHex(hashBytes).toUpperCase(); + const hashStart = hash.substr(0, 5); + const hashEnding = hash.substr(5); + + const response = await this.apiService.nativeFetch(new Request(PwnedPasswordsApi + hashStart)); + const leakedHashes = await response.text(); + const match = leakedHashes.split(/\r?\n/).find((v) => { + return v.split(":")[0] === hashEnding; + }); + + return match != null ? parseInt(match.split(":")[1], 10) : 0; + } + + async breachedAccounts(username: string): Promise { + try { + return await this.apiService.getHibpBreach(username); + } catch (e) { + const error = e as ErrorResponse; + if (error.statusCode === 404) { + return []; + } + throw new Error(); + } + } +} diff --git a/jslib/common/src/services/auth.service.ts b/jslib/common/src/services/auth.service.ts new file mode 100644 index 00000000..fe4542b8 --- /dev/null +++ b/jslib/common/src/services/auth.service.ts @@ -0,0 +1,198 @@ +import { ApiService } from "../abstractions/api.service"; +import { AppIdService } from "../abstractions/appId.service"; +import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { TwoFactorService } from "../abstractions/twoFactor.service"; +import { AuthenticationType } from "../enums/authenticationType"; +import { KdfType } from "../enums/kdfType"; +import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy"; +import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy"; +import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; +import { AuthResult } from "../models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; +import { PreloginRequest } from "../models/request/preloginRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; + +const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes + +export class AuthService implements AuthServiceAbstraction { + get email(): string { + return this.logInStrategy instanceof PasswordLogInStrategy ? this.logInStrategy.email : null; + } + + get masterPasswordHash(): string { + return this.logInStrategy instanceof PasswordLogInStrategy + ? this.logInStrategy.masterPasswordHash + : null; + } + + private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + private sessionTimeout: any; + + constructor( + protected cryptoService: CryptoService, + protected apiService: ApiService, + protected tokenService: TokenService, + protected appIdService: AppIdService, + protected platformUtilsService: PlatformUtilsService, + protected messagingService: MessagingService, + protected logService: LogService, + protected keyConnectorService: KeyConnectorService, + protected environmentService: EnvironmentService, + protected stateService: StateService, + protected twoFactorService: TwoFactorService, + protected i18nService: I18nService + ) {} + + async logIn( + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + ): Promise { + this.clearState(); + + let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + + if (credentials.type === AuthenticationType.Password) { + strategy = new PasswordLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this + ); + } else if (credentials.type === AuthenticationType.Sso) { + strategy = new SsoLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.keyConnectorService + ); + } else if (credentials.type === AuthenticationType.Api) { + strategy = new ApiLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.environmentService, + this.keyConnectorService + ); + } + + const result = await strategy.logIn(credentials as any); + + if (result?.requiresTwoFactor) { + this.saveState(strategy); + } + return result; + } + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ): Promise { + if (this.logInStrategy == null) { + throw new Error(this.i18nService.t("sessionTimeout")); + } + + try { + const result = await this.logInStrategy.logInTwoFactor(twoFactor, captchaResponse); + + // Only clear state if 2FA token has been accepted, otherwise we need to be able to try again + if (!result.requiresTwoFactor && !result.requiresCaptcha) { + this.clearState(); + } + return result; + } catch (e) { + // API exceptions are okay, but if there are any unhandled client-side errors then clear state to be safe + if (!(e instanceof ErrorResponse)) { + this.clearState(); + } + throw e; + } + } + + logOut(callback: () => void) { + callback(); + this.messagingService.send("loggedOut"); + } + + authingWithApiKey(): boolean { + return this.logInStrategy instanceof ApiLogInStrategy; + } + + authingWithSso(): boolean { + return this.logInStrategy instanceof SsoLogInStrategy; + } + + authingWithPassword(): boolean { + return this.logInStrategy instanceof PasswordLogInStrategy; + } + + async makePreloginKey(masterPassword: string, email: string): Promise { + email = email.trim().toLowerCase(); + let kdf: KdfType = null; + let kdfIterations: number = null; + try { + const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email)); + if (preloginResponse != null) { + kdf = preloginResponse.kdf; + kdfIterations = preloginResponse.kdfIterations; + } + } catch (e) { + if (e == null || e.statusCode !== 404) { + throw e; + } + } + return this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations); + } + + private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) { + this.logInStrategy = strategy; + this.startSessionTimeout(); + } + + private clearState() { + this.logInStrategy = null; + this.clearSessionTimeout(); + } + + private startSessionTimeout() { + this.clearSessionTimeout(); + this.sessionTimeout = setTimeout(() => this.clearState(), sessionTimeoutLength); + } + + private clearSessionTimeout() { + if (this.sessionTimeout != null) { + clearTimeout(this.sessionTimeout); + } + } +} diff --git a/jslib/common/src/services/azureFileUpload.service.ts b/jslib/common/src/services/azureFileUpload.service.ts new file mode 100644 index 00000000..d7017bfb --- /dev/null +++ b/jslib/common/src/services/azureFileUpload.service.ts @@ -0,0 +1,214 @@ +import { LogService } from "../abstractions/log.service"; +import { Utils } from "../misc/utils"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; + +const MAX_SINGLE_BLOB_UPLOAD_SIZE = 256 * 1024 * 1024; // 256 MiB +const MAX_BLOCKS_PER_BLOB = 50000; + +export class AzureFileUploadService { + constructor(private logService: LogService) {} + + async upload(url: string, data: EncArrayBuffer, renewalCallback: () => Promise) { + if (data.buffer.byteLength <= MAX_SINGLE_BLOB_UPLOAD_SIZE) { + return await this.azureUploadBlob(url, data); + } else { + return await this.azureUploadBlocks(url, data, renewalCallback); + } + } + private async azureUploadBlob(url: string, data: EncArrayBuffer) { + const urlObject = Utils.getUrl(url); + const headers = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": urlObject.searchParams.get("sv"), + "Content-Length": data.buffer.byteLength.toString(), + "x-ms-blob-type": "BlockBlob", + }); + + const request = new Request(url, { + body: data.buffer, + cache: "no-store", + method: "PUT", + headers: headers, + }); + + const blobResponse = await fetch(request); + + if (blobResponse.status !== 201) { + throw new Error(`Failed to create Azure blob: ${blobResponse.status}`); + } + } + private async azureUploadBlocks( + url: string, + data: EncArrayBuffer, + renewalCallback: () => Promise + ) { + const baseUrl = Utils.getUrl(url); + const blockSize = this.getMaxBlockSize(baseUrl.searchParams.get("sv")); + let blockIndex = 0; + const numBlocks = Math.ceil(data.buffer.byteLength / blockSize); + const blocksStaged: string[] = []; + + if (numBlocks > MAX_BLOCKS_PER_BLOB) { + throw new Error( + `Cannot upload file, exceeds maximum size of ${blockSize * MAX_BLOCKS_PER_BLOB}` + ); + } + + // eslint-disable-next-line + try { + while (blockIndex < numBlocks) { + url = await this.renewUrlIfNecessary(url, renewalCallback); + const blockUrl = Utils.getUrl(url); + const blockId = this.encodedBlockId(blockIndex); + blockUrl.searchParams.append("comp", "block"); + blockUrl.searchParams.append("blockid", blockId); + const start = blockIndex * blockSize; + const blockData = data.buffer.slice(start, start + blockSize); + const blockHeaders = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": blockUrl.searchParams.get("sv"), + "Content-Length": blockData.byteLength.toString(), + }); + + const blockRequest = new Request(blockUrl.toString(), { + body: blockData, + cache: "no-store", + method: "PUT", + headers: blockHeaders, + }); + + const blockResponse = await fetch(blockRequest); + + if (blockResponse.status !== 201) { + const message = `Unsuccessful block PUT. Received status ${blockResponse.status}`; + this.logService.error(message + "\n" + (await blockResponse.json())); + throw new Error(message); + } + + blocksStaged.push(blockId); + blockIndex++; + } + + url = await this.renewUrlIfNecessary(url, renewalCallback); + const blockListUrl = Utils.getUrl(url); + const blockListXml = this.blockListXml(blocksStaged); + blockListUrl.searchParams.append("comp", "blocklist"); + const headers = new Headers({ + "x-ms-date": new Date().toUTCString(), + "x-ms-version": blockListUrl.searchParams.get("sv"), + "Content-Length": blockListXml.length.toString(), + }); + + const request = new Request(blockListUrl.toString(), { + body: blockListXml, + cache: "no-store", + method: "PUT", + headers: headers, + }); + + const response = await fetch(request); + + if (response.status !== 201) { + const message = `Unsuccessful block list PUT. Received status ${response.status}`; + this.logService.error(message + "\n" + (await response.json())); + throw new Error(message); + } + } catch (e) { + throw e; + } + } + + private async renewUrlIfNecessary( + url: string, + renewalCallback: () => Promise + ): Promise { + const urlObject = Utils.getUrl(url); + const expiry = new Date(urlObject.searchParams.get("se") ?? ""); + + if (isNaN(expiry.getTime())) { + expiry.setTime(Date.now() + 3600000); + } + + if (expiry.getTime() < Date.now() + 1000) { + return await renewalCallback(); + } + return url; + } + + private encodedBlockId(blockIndex: number) { + // Encoded blockId max size is 64, so pre-encoding max size is 48 + const utfBlockId = ( + "000000000000000000000000000000000000000000000000" + blockIndex.toString() + ).slice(-48); + return Utils.fromUtf8ToB64(utfBlockId); + } + + private blockListXml(blockIdList: string[]) { + let xml = ''; + blockIdList.forEach((blockId) => { + xml += `${blockId}`; + }); + xml += ""; + return xml; + } + + private getMaxBlockSize(version: string) { + if (Version.compare(version, "2019-12-12") >= 0) { + return 4000 * 1024 * 1024; // 4000 MiB + } else if (Version.compare(version, "2016-05-31") >= 0) { + return 100 * 1024 * 1024; // 100 MiB + } else { + return 4 * 1024 * 1024; // 4 MiB + } + } +} + +class Version { + /** + * Compares two Azure Versions against each other + * @param a Version to compare + * @param b Version to compare + * @returns a number less than zero if b is newer than a, 0 if equal, + * and greater than zero if a is newer than b + */ + static compare(a: Required | string, b: Required | string) { + if (typeof a === "string") { + a = new Version(a); + } + + if (typeof b === "string") { + b = new Version(b); + } + + return a.year !== b.year + ? a.year - b.year + : a.month !== b.month + ? a.month - b.month + : a.day !== b.day + ? a.day - b.day + : 0; + } + year = 0; + month = 0; + day = 0; + + constructor(version: string) { + try { + const parts = version.split("-").map((v) => Number.parseInt(v, 10)); + this.year = parts[0]; + this.month = parts[1]; + this.day = parts[2]; + } catch { + // Ignore error + } + } + /** + * Compares two Azure Versions against each other + * @param compareTo Version to compare against + * @returns a number less than zero if compareTo is newer, 0 if equal, + * and greater than zero if this is greater than compareTo + */ + compare(compareTo: Required | string) { + return Version.compare(this, compareTo); + } +} diff --git a/jslib/common/src/services/bitwardenFileUpload.service.ts b/jslib/common/src/services/bitwardenFileUpload.service.ts new file mode 100644 index 00000000..dd189e3e --- /dev/null +++ b/jslib/common/src/services/bitwardenFileUpload.service.ts @@ -0,0 +1,34 @@ +import { ApiService } from "../abstractions/api.service"; +import { Utils } from "../misc/utils"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; + +export class BitwardenFileUploadService { + constructor(private apiService: ApiService) {} + + async upload( + encryptedFileName: string, + encryptedFileData: EncArrayBuffer, + apiCall: (fd: FormData) => Promise + ) { + const fd = new FormData(); + try { + const blob = new Blob([encryptedFileData.buffer], { type: "application/octet-stream" }); + fd.append("data", blob, encryptedFileName); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append( + "data", + Buffer.from(encryptedFileData.buffer) as any, + { + filepath: encryptedFileName, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + await apiCall(fd); + } +} diff --git a/jslib/common/src/services/broadcaster.service.ts b/jslib/common/src/services/broadcaster.service.ts new file mode 100644 index 00000000..999fbe4b --- /dev/null +++ b/jslib/common/src/services/broadcaster.service.ts @@ -0,0 +1,28 @@ +import { BroadcasterService as BroadcasterServiceAbstraction } from "../abstractions/broadcaster.service"; + +export class BroadcasterService implements BroadcasterServiceAbstraction { + subscribers: Map any> = new Map any>(); + + send(message: any, id?: string) { + if (id != null) { + if (this.subscribers.has(id)) { + this.subscribers.get(id)(message); + } + return; + } + + this.subscribers.forEach((value) => { + value(message); + }); + } + + subscribe(id: string, messageCallback: (message: any) => any) { + this.subscribers.set(id, messageCallback); + } + + unsubscribe(id: string) { + if (this.subscribers.has(id)) { + this.subscribers.delete(id); + } + } +} diff --git a/jslib/common/src/services/cipher.service.ts b/jslib/common/src/services/cipher.service.ts new file mode 100644 index 00000000..1deeec8e --- /dev/null +++ b/jslib/common/src/services/cipher.service.ts @@ -0,0 +1,1282 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FileUploadService } from "../abstractions/fileUpload.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { LogService } from "../abstractions/log.service"; +import { SearchService } from "../abstractions/search.service"; +import { SettingsService } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { sequentialize } from "../misc/sequentialize"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { Attachment } from "../models/domain/attachment"; +import { Card } from "../models/domain/card"; +import { Cipher } from "../models/domain/cipher"; +import Domain from "../models/domain/domainBase"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { Field } from "../models/domain/field"; +import { Identity } from "../models/domain/identity"; +import { Login } from "../models/domain/login"; +import { LoginUri } from "../models/domain/loginUri"; +import { Password } from "../models/domain/password"; +import { SecureNote } from "../models/domain/secureNote"; +import { SortedCiphersCache } from "../models/domain/sortedCiphersCache"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { AttachmentRequest } from "../models/request/attachmentRequest"; +import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest"; +import { CipherBulkMoveRequest } from "../models/request/cipherBulkMoveRequest"; +import { CipherBulkRestoreRequest } from "../models/request/cipherBulkRestoreRequest"; +import { CipherBulkShareRequest } from "../models/request/cipherBulkShareRequest"; +import { CipherCollectionsRequest } from "../models/request/cipherCollectionsRequest"; +import { CipherCreateRequest } from "../models/request/cipherCreateRequest"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CipherShareRequest } from "../models/request/cipherShareRequest"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { AttachmentView } from "../models/view/attachmentView"; +import { CipherView } from "../models/view/cipherView"; +import { FieldView } from "../models/view/fieldView"; +import { PasswordHistoryView } from "../models/view/passwordHistoryView"; +import { View } from "../models/view/view"; + +const DomainMatchBlacklist = new Map>([ + ["google.com", new Set(["script.google.com"])], +]); + +export class CipherService implements CipherServiceAbstraction { + private sortedCiphersCache: SortedCiphersCache = new SortedCiphersCache( + this.sortCiphersByLastUsed + ); + + constructor( + private cryptoService: CryptoService, + private settingsService: SettingsService, + private apiService: ApiService, + private fileUploadService: FileUploadService, + private i18nService: I18nService, + private searchService: () => SearchService, + private logService: LogService, + private stateService: StateService + ) {} + + async getDecryptedCipherCache(): Promise { + const decryptedCiphers = await this.stateService.getDecryptedCiphers(); + return decryptedCiphers; + } + + async setDecryptedCipherCache(value: CipherView[]) { + await this.stateService.setDecryptedCiphers(value); + if (this.searchService != null) { + if (value == null) { + this.searchService().clearIndex(); + } else { + this.searchService().indexCiphers(); + } + } + } + + async clearCache(userId?: string): Promise { + await this.clearDecryptedCiphersState(userId); + } + + async encrypt( + model: CipherView, + key?: SymmetricCryptoKey, + originalCipher: Cipher = null + ): Promise { + // Adjust password history + if (model.id != null) { + if (originalCipher == null) { + originalCipher = await this.get(model.id); + } + if (originalCipher != null) { + const existingCipher = await originalCipher.decrypt(); + model.passwordHistory = existingCipher.passwordHistory || []; + if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) { + if ( + existingCipher.login.password != null && + existingCipher.login.password !== "" && + existingCipher.login.password !== model.login.password + ) { + const ph = new PasswordHistoryView(); + ph.password = existingCipher.login.password; + ph.lastUsedDate = model.login.passwordRevisionDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } else { + model.login.passwordRevisionDate = existingCipher.login.passwordRevisionDate; + } + } + if (existingCipher.hasFields) { + const existingHiddenFields = existingCipher.fields.filter( + (f) => + f.type === FieldType.Hidden && + f.name != null && + f.name !== "" && + f.value != null && + f.value !== "" + ); + const hiddenFields = + model.fields == null + ? [] + : model.fields.filter( + (f) => f.type === FieldType.Hidden && f.name != null && f.name !== "" + ); + existingHiddenFields.forEach((ef) => { + const matchedField = hiddenFields.find((f) => f.name === ef.name); + if (matchedField == null || matchedField.value !== ef.value) { + const ph = new PasswordHistoryView(); + ph.password = ef.name + ": " + ef.value; + ph.lastUsedDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } + }); + } + } + if (model.passwordHistory != null && model.passwordHistory.length === 0) { + model.passwordHistory = null; + } else if (model.passwordHistory != null && model.passwordHistory.length > 5) { + // only save last 5 history + model.passwordHistory = model.passwordHistory.slice(0, 5); + } + } + + const cipher = new Cipher(); + cipher.id = model.id; + cipher.folderId = model.folderId; + cipher.favorite = model.favorite; + cipher.organizationId = model.organizationId; + cipher.type = model.type; + cipher.collectionIds = model.collectionIds; + cipher.revisionDate = model.revisionDate; + cipher.reprompt = model.reprompt; + + if (key == null && cipher.organizationId != null) { + key = await this.cryptoService.getOrgKey(cipher.organizationId); + if (key == null) { + throw new Error("Cannot encrypt cipher for organization. No key."); + } + } + await Promise.all([ + this.encryptObjProperty( + model, + cipher, + { + name: null, + notes: null, + }, + key + ), + this.encryptCipherData(cipher, model, key), + this.encryptFields(model.fields, key).then((fields) => { + cipher.fields = fields; + }), + this.encryptPasswordHistories(model.passwordHistory, key).then((ph) => { + cipher.passwordHistory = ph; + }), + this.encryptAttachments(model.attachments, key).then((attachments) => { + cipher.attachments = attachments; + }), + ]); + + return cipher; + } + + async encryptAttachments( + attachmentsModel: AttachmentView[], + key: SymmetricCryptoKey + ): Promise { + if (attachmentsModel == null || attachmentsModel.length === 0) { + return null; + } + + const promises: Promise[] = []; + const encAttachments: Attachment[] = []; + attachmentsModel.forEach(async (model) => { + const attachment = new Attachment(); + attachment.id = model.id; + attachment.size = model.size; + attachment.sizeName = model.sizeName; + attachment.url = model.url; + const promise = this.encryptObjProperty( + model, + attachment, + { + fileName: null, + }, + key + ).then(async () => { + if (model.key != null) { + attachment.key = await this.cryptoService.encrypt(model.key.key, key); + } + encAttachments.push(attachment); + }); + promises.push(promise); + }); + + await Promise.all(promises); + return encAttachments; + } + + async encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise { + if (!fieldsModel || !fieldsModel.length) { + return null; + } + + const self = this; + const encFields: Field[] = []; + await fieldsModel.reduce(async (promise, field) => { + await promise; + const encField = await self.encryptField(field, key); + encFields.push(encField); + }, Promise.resolve()); + + return encFields; + } + + async encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise { + const field = new Field(); + field.type = fieldModel.type; + field.linkedId = fieldModel.linkedId; + // normalize boolean type field values + if (fieldModel.type === FieldType.Boolean && fieldModel.value !== "true") { + fieldModel.value = "false"; + } + + await this.encryptObjProperty( + fieldModel, + field, + { + name: null, + value: null, + }, + key + ); + + return field; + } + + async encryptPasswordHistories( + phModels: PasswordHistoryView[], + key: SymmetricCryptoKey + ): Promise { + if (!phModels || !phModels.length) { + return null; + } + + const self = this; + const encPhs: Password[] = []; + await phModels.reduce(async (promise, ph) => { + await promise; + const encPh = await self.encryptPasswordHistory(ph, key); + encPhs.push(encPh); + }, Promise.resolve()); + + return encPhs; + } + + async encryptPasswordHistory( + phModel: PasswordHistoryView, + key: SymmetricCryptoKey + ): Promise { + const ph = new Password(); + ph.lastUsedDate = phModel.lastUsedDate; + + await this.encryptObjProperty( + phModel, + ph, + { + password: null, + }, + key + ); + + return ph; + } + + async get(id: string): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + // eslint-disable-next-line + if (ciphers == null || !ciphers.hasOwnProperty(id)) { + return null; + } + + const localData = await this.stateService.getLocalData(); + return new Cipher(ciphers[id], localData ? localData[id] : null); + } + + async getAll(): Promise { + const localData = await this.stateService.getLocalData(); + const ciphers = await this.stateService.getEncryptedCiphers(); + const response: Cipher[] = []; + for (const id in ciphers) { + // eslint-disable-next-line + if (ciphers.hasOwnProperty(id)) { + response.push(new Cipher(ciphers[id], localData ? localData[id] : null)); + } + } + return response; + } + + @sequentialize(() => "getAllDecrypted") + async getAllDecrypted(): Promise { + const userId = await this.stateService.getUserId(); + if ((await this.getDecryptedCipherCache()) != null) { + if ( + this.searchService != null && + (this.searchService().indexedEntityId ?? userId) !== userId + ) { + await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache()); + } + return await this.getDecryptedCipherCache(); + } + + const decCiphers: CipherView[] = []; + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const promises: any[] = []; + const ciphers = await this.getAll(); + ciphers.forEach(async (cipher) => { + promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); + }); + + await Promise.all(promises); + decCiphers.sort(this.getLocaleSortingFunction()); + await this.setDecryptedCipherCache(decCiphers); + return decCiphers; + } + + async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise { + const ciphers = await this.getAllDecrypted(); + + return ciphers.filter((cipher) => { + if (cipher.isDeleted) { + return false; + } + if (folder && cipher.folderId === groupingId) { + return true; + } else if ( + !folder && + cipher.collectionIds != null && + cipher.collectionIds.indexOf(groupingId) > -1 + ) { + return true; + } + + return false; + }); + } + + async getAllDecryptedForUrl( + url: string, + includeOtherTypes?: CipherType[], + defaultMatch: UriMatchType = null + ): Promise { + if (url == null && includeOtherTypes == null) { + return Promise.resolve([]); + } + + const domain = Utils.getDomain(url); + const eqDomainsPromise = + domain == null + ? Promise.resolve([]) + : this.settingsService.getEquivalentDomains().then((eqDomains: any[][]) => { + let matches: any[] = []; + eqDomains.forEach((eqDomain) => { + if (eqDomain.length && eqDomain.indexOf(domain) >= 0) { + matches = matches.concat(eqDomain); + } + }); + + if (!matches.length) { + matches.push(domain); + } + + return matches; + }); + + const result = await Promise.all([eqDomainsPromise, this.getAllDecrypted()]); + const matchingDomains = result[0]; + const ciphers = result[1]; + + if (defaultMatch == null) { + defaultMatch = await this.stateService.getDefaultUriMatch(); + if (defaultMatch == null) { + defaultMatch = UriMatchType.Domain; + } + } + + return ciphers.filter((cipher) => { + if (cipher.deletedDate != null) { + return false; + } + if (includeOtherTypes != null && includeOtherTypes.indexOf(cipher.type) > -1) { + return true; + } + + if (url != null && cipher.type === CipherType.Login && cipher.login.uris != null) { + for (let i = 0; i < cipher.login.uris.length; i++) { + const u = cipher.login.uris[i]; + if (u.uri == null) { + continue; + } + + const match = u.match == null ? defaultMatch : u.match; + switch (match) { + case UriMatchType.Domain: + if (domain != null && u.domain != null && matchingDomains.indexOf(u.domain) > -1) { + if (DomainMatchBlacklist.has(u.domain)) { + const domainUrlHost = Utils.getHost(url); + if (!DomainMatchBlacklist.get(u.domain).has(domainUrlHost)) { + return true; + } + } else { + return true; + } + } + break; + case UriMatchType.Host: { + const urlHost = Utils.getHost(url); + if (urlHost != null && urlHost === Utils.getHost(u.uri)) { + return true; + } + break; + } + case UriMatchType.Exact: + if (url === u.uri) { + return true; + } + break; + case UriMatchType.StartsWith: + if (url.startsWith(u.uri)) { + return true; + } + break; + case UriMatchType.RegularExpression: + try { + const regex = new RegExp(u.uri, "i"); + if (regex.test(url)) { + return true; + } + } catch (e) { + this.logService.error(e); + } + break; + case UriMatchType.Never: + default: + break; + } + } + } + + return false; + }); + } + + async getAllFromApiForOrganization(organizationId: string): Promise { + const ciphers = await this.apiService.getCiphersOrganization(organizationId); + if (ciphers != null && ciphers.data != null && ciphers.data.length) { + const decCiphers: CipherView[] = []; + const promises: any[] = []; + ciphers.data.forEach((r) => { + const data = new CipherData(r); + const cipher = new Cipher(data); + promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); + }); + await Promise.all(promises); + decCiphers.sort(this.getLocaleSortingFunction()); + return decCiphers; + } else { + return []; + } + } + + async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise { + return this.getCipherForUrl(url, true, false, autofillOnPageLoad); + } + + async getLastLaunchedForUrl(url: string, autofillOnPageLoad = false): Promise { + return this.getCipherForUrl(url, false, true, autofillOnPageLoad); + } + + async getNextCipherForUrl(url: string): Promise { + return this.getCipherForUrl(url, false, false, false); + } + + updateLastUsedIndexForUrl(url: string) { + this.sortedCiphersCache.updateLastUsedIndex(url); + } + + async updateLastUsedDate(id: string): Promise { + let ciphersLocalData = await this.stateService.getLocalData(); + if (!ciphersLocalData) { + ciphersLocalData = {}; + } + + if (ciphersLocalData[id]) { + ciphersLocalData[id].lastUsedDate = new Date().getTime(); + } else { + ciphersLocalData[id] = { + lastUsedDate: new Date().getTime(), + }; + } + + await this.stateService.setLocalData(ciphersLocalData); + + const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); + if (!decryptedCipherCache) { + return; + } + + for (let i = 0; i < decryptedCipherCache.length; i++) { + const cached = decryptedCipherCache[i]; + if (cached.id === id) { + cached.localData = ciphersLocalData[id]; + break; + } + } + await this.stateService.setDecryptedCiphers(decryptedCipherCache); + } + + async updateLastLaunchedDate(id: string): Promise { + let ciphersLocalData = await this.stateService.getLocalData(); + if (!ciphersLocalData) { + ciphersLocalData = {}; + } + + if (ciphersLocalData[id]) { + ciphersLocalData[id].lastLaunched = new Date().getTime(); + } else { + ciphersLocalData[id] = { + lastUsedDate: new Date().getTime(), + }; + } + + await this.stateService.setLocalData(ciphersLocalData); + + const decryptedCipherCache = await this.stateService.getDecryptedCiphers(); + if (!decryptedCipherCache) { + return; + } + + for (let i = 0; i < decryptedCipherCache.length; i++) { + const cached = decryptedCipherCache[i]; + if (cached.id === id) { + cached.localData = ciphersLocalData[id]; + break; + } + } + await this.stateService.setDecryptedCiphers(decryptedCipherCache); + } + + async saveNeverDomain(domain: string): Promise { + if (domain == null) { + return; + } + + let domains = await this.stateService.getNeverDomains(); + if (!domains) { + domains = {}; + } + domains[domain] = null; + await this.stateService.setNeverDomains(domains); + } + + async saveWithServer(cipher: Cipher): Promise { + let response: CipherResponse; + if (cipher.id == null) { + if (cipher.collectionIds != null) { + const request = new CipherCreateRequest(cipher); + response = await this.apiService.postCipherCreate(request); + } else { + const request = new CipherRequest(cipher); + response = await this.apiService.postCipher(request); + } + cipher.id = response.id; + } else { + const request = new CipherRequest(cipher); + response = await this.apiService.putCipher(cipher.id, request); + } + + const data = new CipherData( + response, + await this.stateService.getUserId(), + cipher.collectionIds + ); + await this.upsert(data); + } + + async shareWithServer( + cipher: CipherView, + organizationId: string, + collectionIds: string[] + ): Promise { + const attachmentPromises: Promise[] = []; + if (cipher.attachments != null) { + cipher.attachments.forEach((attachment) => { + if (attachment.key == null) { + attachmentPromises.push( + this.shareAttachmentWithServer(attachment, cipher.id, organizationId) + ); + } + }); + } + await Promise.all(attachmentPromises); + + cipher.organizationId = organizationId; + cipher.collectionIds = collectionIds; + const encCipher = await this.encrypt(cipher); + const request = new CipherShareRequest(encCipher); + const response = await this.apiService.putShareCipher(cipher.id, request); + const data = new CipherData(response, await this.stateService.getUserId(), collectionIds); + await this.upsert(data); + } + + async shareManyWithServer( + ciphers: CipherView[], + organizationId: string, + collectionIds: string[] + ): Promise { + const promises: Promise[] = []; + const encCiphers: Cipher[] = []; + for (const cipher of ciphers) { + cipher.organizationId = organizationId; + cipher.collectionIds = collectionIds; + promises.push( + this.encrypt(cipher).then((c) => { + encCiphers.push(c); + }) + ); + } + await Promise.all(promises); + const request = new CipherBulkShareRequest(encCiphers, collectionIds); + try { + await this.apiService.putShareCiphers(request); + } catch (e) { + for (const cipher of ciphers) { + cipher.organizationId = null; + cipher.collectionIds = null; + } + throw e; + } + const userId = await this.stateService.getUserId(); + await this.upsert(encCiphers.map((c) => c.toCipherData(userId))); + } + + saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsArrayBuffer(unencryptedFile); + reader.onload = async (evt: any) => { + try { + const cData = await this.saveAttachmentRawWithServer( + cipher, + unencryptedFile.name, + evt.target.result, + admin + ); + resolve(cData); + } catch (e) { + reject(e); + } + }; + reader.onerror = () => { + reject("Error reading file."); + }; + }); + } + + async saveAttachmentRawWithServer( + cipher: Cipher, + filename: string, + data: ArrayBuffer, + admin = false + ): Promise { + const key = await this.cryptoService.getOrgKey(cipher.organizationId); + const encFileName = await this.cryptoService.encrypt(filename, key); + + const dataEncKey = await this.cryptoService.makeEncKey(key); + const encData = await this.cryptoService.encryptToBytes(data, dataEncKey[0]); + + const request: AttachmentRequest = { + key: dataEncKey[1].encryptedString, + fileName: encFileName.encryptedString, + fileSize: encData.buffer.byteLength, + adminRequest: admin, + }; + + let response: CipherResponse; + try { + const uploadDataResponse = await this.apiService.postCipherAttachment(cipher.id, request); + response = admin ? uploadDataResponse.cipherMiniResponse : uploadDataResponse.cipherResponse; + await this.fileUploadService.uploadCipherAttachment( + admin, + uploadDataResponse, + encFileName, + encData + ); + } catch (e) { + if ( + (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) || + (e as ErrorResponse).statusCode === 405 + ) { + response = await this.legacyServerAttachmentFileUpload( + admin, + cipher.id, + encFileName, + encData, + dataEncKey[1] + ); + } else if (e instanceof ErrorResponse) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } else { + throw e; + } + } + + const cData = new CipherData( + response, + await this.stateService.getUserId(), + cipher.collectionIds + ); + if (!admin) { + await this.upsert(cData); + } + return new Cipher(cData); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async legacyServerAttachmentFileUpload( + admin: boolean, + cipherId: string, + encFileName: EncString, + encData: EncArrayBuffer, + key: EncString + ) { + const fd = new FormData(); + try { + const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); + fd.append("key", key.encryptedString); + fd.append("data", blob, encFileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("key", key.encryptedString); + fd.append( + "data", + Buffer.from(encData.buffer) as any, + { + filepath: encFileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + let response: CipherResponse; + try { + if (admin) { + response = await this.apiService.postCipherAttachmentAdminLegacy(cipherId, fd); + } else { + response = await this.apiService.postCipherAttachmentLegacy(cipherId, fd); + } + } catch (e) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } + + return response; + } + + async saveCollectionsWithServer(cipher: Cipher): Promise { + const request = new CipherCollectionsRequest(cipher.collectionIds); + await this.apiService.putCipherCollections(cipher.id, request); + const data = cipher.toCipherData(await this.stateService.getUserId()); + await this.upsert(data); + } + + async upsert(cipher: CipherData | CipherData[]): Promise { + let ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + ciphers = {}; + } + + if (cipher instanceof CipherData) { + const c = cipher as CipherData; + ciphers[c.id] = c; + } else { + (cipher as CipherData[]).forEach((c) => { + ciphers[c.id] = c; + }); + } + + await this.replace(ciphers); + } + + async replace(ciphers: { [id: string]: CipherData }): Promise { + await this.clearDecryptedCiphersState(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async clear(userId?: string): Promise { + await this.clearEncryptedCiphersState(userId); + await this.clearCache(userId); + } + + async moveManyWithServer(ids: string[], folderId: string): Promise { + await this.apiService.putMoveCiphers(new CipherBulkMoveRequest(ids, folderId)); + + let ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + ciphers = {}; + } + + ids.forEach((id) => { + // eslint-disable-next-line + if (ciphers.hasOwnProperty(id)) { + ciphers[id].folderId = folderId; + } + }); + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async delete(id: string | string[]): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + if (typeof id === "string") { + if (ciphers[id] == null) { + return; + } + delete ciphers[id]; + } else { + (id as string[]).forEach((i) => { + delete ciphers[i]; + }); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteCipher(id); + await this.delete(id); + } + + async deleteManyWithServer(ids: string[]): Promise { + await this.apiService.deleteManyCiphers(new CipherBulkDeleteRequest(ids)); + await this.delete(ids); + } + + async deleteAttachment(id: string, attachmentId: string): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + + // eslint-disable-next-line + if (ciphers == null || !ciphers.hasOwnProperty(id) || ciphers[id].attachments == null) { + return; + } + + for (let i = 0; i < ciphers[id].attachments.length; i++) { + if (ciphers[id].attachments[i].id === attachmentId) { + ciphers[id].attachments.splice(i, 1); + } + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async deleteAttachmentWithServer(id: string, attachmentId: string): Promise { + try { + await this.apiService.deleteCipherAttachment(id, attachmentId); + } catch (e) { + return Promise.reject((e as ErrorResponse).getSingleMessage()); + } + await this.deleteAttachment(id, attachmentId); + } + + sortCiphersByLastUsed(a: CipherView, b: CipherView): number { + const aLastUsed = + a.localData && a.localData.lastUsedDate ? (a.localData.lastUsedDate as number) : null; + const bLastUsed = + b.localData && b.localData.lastUsedDate ? (b.localData.lastUsedDate as number) : null; + + const bothNotNull = aLastUsed != null && bLastUsed != null; + if (bothNotNull && aLastUsed < bLastUsed) { + return 1; + } + if (aLastUsed != null && bLastUsed == null) { + return -1; + } + + if (bothNotNull && aLastUsed > bLastUsed) { + return -1; + } + if (bLastUsed != null && aLastUsed == null) { + return 1; + } + + return 0; + } + + sortCiphersByLastUsedThenName(a: CipherView, b: CipherView): number { + const result = this.sortCiphersByLastUsed(a, b); + if (result !== 0) { + return result; + } + + return this.getLocaleSortingFunction()(a, b); + } + + getLocaleSortingFunction(): (a: CipherView, b: CipherView) => number { + return (a, b) => { + let aName = a.name; + let bName = b.name; + + if (aName == null && bName != null) { + return -1; + } + if (aName != null && bName == null) { + return 1; + } + if (aName == null && bName == null) { + return 0; + } + + const result = this.i18nService.collator + ? this.i18nService.collator.compare(aName, bName) + : aName.localeCompare(bName); + + if (result !== 0 || a.type !== CipherType.Login || b.type !== CipherType.Login) { + return result; + } + + if (a.login.username != null) { + aName += a.login.username; + } + + if (b.login.username != null) { + bName += b.login.username; + } + + return this.i18nService.collator + ? this.i18nService.collator.compare(aName, bName) + : aName.localeCompare(bName); + }; + } + + async softDelete(id: string | string[]): Promise { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + const setDeletedDate = (cipherId: string) => { + if (ciphers[cipherId] == null) { + return; + } + ciphers[cipherId].deletedDate = new Date().toISOString(); + }; + + if (typeof id === "string") { + setDeletedDate(id); + } else { + (id as string[]).forEach(setDeletedDate); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async softDeleteWithServer(id: string): Promise { + await this.apiService.putDeleteCipher(id); + await this.softDelete(id); + } + + async softDeleteManyWithServer(ids: string[]): Promise { + await this.apiService.putDeleteManyCiphers(new CipherBulkDeleteRequest(ids)); + await this.softDelete(ids); + } + + async restore( + cipher: { id: string; revisionDate: string } | { id: string; revisionDate: string }[] + ) { + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers == null) { + return; + } + + const clearDeletedDate = (c: { id: string; revisionDate: string }) => { + if (ciphers[c.id] == null) { + return; + } + ciphers[c.id].deletedDate = null; + ciphers[c.id].revisionDate = c.revisionDate; + }; + + if (cipher.constructor.name === Array.name) { + (cipher as { id: string; revisionDate: string }[]).forEach(clearDeletedDate); + } else { + clearDeletedDate(cipher as { id: string; revisionDate: string }); + } + + await this.clearCache(); + await this.stateService.setEncryptedCiphers(ciphers); + } + + async restoreWithServer(id: string): Promise { + const response = await this.apiService.putRestoreCipher(id); + await this.restore({ id: id, revisionDate: response.revisionDate }); + } + + async restoreManyWithServer(ids: string[]): Promise { + const response = await this.apiService.putRestoreManyCiphers(new CipherBulkRestoreRequest(ids)); + const restores: { id: string; revisionDate: string }[] = []; + for (const cipher of response.data) { + restores.push({ id: cipher.id, revisionDate: cipher.revisionDate }); + } + await this.restore(restores); + } + + // Helpers + + private async shareAttachmentWithServer( + attachmentView: AttachmentView, + cipherId: string, + organizationId: string + ): Promise { + const attachmentResponse = await this.apiService.nativeFetch( + new Request(attachmentView.url, { cache: "no-store" }) + ); + if (attachmentResponse.status !== 200) { + throw Error("Failed to download attachment: " + attachmentResponse.status.toString()); + } + + const buf = await attachmentResponse.arrayBuffer(); + const decBuf = await this.cryptoService.decryptFromBytes(buf, null); + const key = await this.cryptoService.getOrgKey(organizationId); + const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key); + + const dataEncKey = await this.cryptoService.makeEncKey(key); + const encData = await this.cryptoService.encryptToBytes(decBuf, dataEncKey[0]); + + const fd = new FormData(); + try { + const blob = new Blob([encData.buffer], { type: "application/octet-stream" }); + fd.append("key", dataEncKey[1].encryptedString); + fd.append("data", blob, encFileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("key", dataEncKey[1].encryptedString); + fd.append( + "data", + Buffer.from(encData.buffer) as any, + { + filepath: encFileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + + try { + await this.apiService.postShareCipherAttachment( + cipherId, + attachmentView.id, + fd, + organizationId + ); + } catch (e) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } + } + + private async encryptObjProperty( + model: V, + obj: D, + map: any, + key: SymmetricCryptoKey + ): Promise { + const promises = []; + const self = this; + + for (const prop in map) { + // eslint-disable-next-line + if (!map.hasOwnProperty(prop)) { + continue; + } + + (function (theProp, theObj) { + const p = Promise.resolve() + .then(() => { + const modelProp = (model as any)[map[theProp] || theProp]; + if (modelProp && modelProp !== "") { + return self.cryptoService.encrypt(modelProp, key); + } + return null; + }) + .then((val: EncString) => { + (theObj as any)[theProp] = val; + }); + promises.push(p); + })(prop, obj); + } + + await Promise.all(promises); + } + + private async encryptCipherData(cipher: Cipher, model: CipherView, key: SymmetricCryptoKey) { + switch (cipher.type) { + case CipherType.Login: + cipher.login = new Login(); + cipher.login.passwordRevisionDate = model.login.passwordRevisionDate; + cipher.login.autofillOnPageLoad = model.login.autofillOnPageLoad; + await this.encryptObjProperty( + model.login, + cipher.login, + { + username: null, + password: null, + totp: null, + }, + key + ); + + if (model.login.uris != null) { + cipher.login.uris = []; + for (let i = 0; i < model.login.uris.length; i++) { + const loginUri = new LoginUri(); + loginUri.match = model.login.uris[i].match; + await this.encryptObjProperty( + model.login.uris[i], + loginUri, + { + uri: null, + }, + key + ); + cipher.login.uris.push(loginUri); + } + } + return; + case CipherType.SecureNote: + cipher.secureNote = new SecureNote(); + cipher.secureNote.type = model.secureNote.type; + return; + case CipherType.Card: + cipher.card = new Card(); + await this.encryptObjProperty( + model.card, + cipher.card, + { + cardholderName: null, + brand: null, + number: null, + expMonth: null, + expYear: null, + code: null, + }, + key + ); + return; + case CipherType.Identity: + cipher.identity = new Identity(); + await this.encryptObjProperty( + model.identity, + cipher.identity, + { + title: null, + firstName: null, + middleName: null, + lastName: null, + address1: null, + address2: null, + address3: null, + city: null, + state: null, + postalCode: null, + country: null, + company: null, + email: null, + phone: null, + ssn: null, + username: null, + passportNumber: null, + licenseNumber: null, + }, + key + ); + return; + default: + throw new Error("Unknown cipher type."); + } + } + + private async getCipherForUrl( + url: string, + lastUsed: boolean, + lastLaunched: boolean, + autofillOnPageLoad: boolean + ): Promise { + const cacheKey = autofillOnPageLoad ? "autofillOnPageLoad-" + url : url; + + if (!this.sortedCiphersCache.isCached(cacheKey)) { + let ciphers = await this.getAllDecryptedForUrl(url); + if (!ciphers) { + return null; + } + + if (autofillOnPageLoad) { + const autofillOnPageLoadDefault = await this.stateService.getAutoFillOnPageLoadDefault(); + ciphers = ciphers.filter( + (cipher) => + cipher.login.autofillOnPageLoad || + (cipher.login.autofillOnPageLoad == null && autofillOnPageLoadDefault !== false) + ); + if (ciphers.length === 0) { + return null; + } + } + + this.sortedCiphersCache.addCiphers(cacheKey, ciphers); + } + + if (lastLaunched) { + return this.sortedCiphersCache.getLastLaunched(cacheKey); + } else if (lastUsed) { + return this.sortedCiphersCache.getLastUsed(cacheKey); + } else { + return this.sortedCiphersCache.getNext(cacheKey); + } + } + + private async clearEncryptedCiphersState(userId?: string) { + await this.stateService.setEncryptedCiphers(null, { userId: userId }); + } + + private async clearDecryptedCiphersState(userId?: string) { + await this.stateService.setDecryptedCiphers(null, { userId: userId }); + this.clearSortedCiphers(); + } + + private clearSortedCiphers() { + this.sortedCiphersCache.clear(); + } +} diff --git a/jslib/common/src/services/collection.service.ts b/jslib/common/src/services/collection.service.ts new file mode 100644 index 00000000..4d3563d7 --- /dev/null +++ b/jslib/common/src/services/collection.service.ts @@ -0,0 +1,157 @@ +import { CollectionService as CollectionServiceAbstraction } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { StateService } from "../abstractions/state.service"; +import { ServiceUtils } from "../misc/serviceUtils"; +import { Utils } from "../misc/utils"; +import { CollectionData } from "../models/data/collectionData"; +import { Collection } from "../models/domain/collection"; +import { TreeNode } from "../models/domain/treeNode"; +import { CollectionView } from "../models/view/collectionView"; + +const NestingDelimiter = "/"; + +export class CollectionService implements CollectionServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private i18nService: I18nService, + private stateService: StateService + ) {} + + async clearCache(userId?: string): Promise { + await this.stateService.setDecryptedCollections(null, { userId: userId }); + } + + async encrypt(model: CollectionView): Promise { + if (model.organizationId == null) { + throw new Error("Collection has no organization id."); + } + const key = await this.cryptoService.getOrgKey(model.organizationId); + if (key == null) { + throw new Error("No key for this collection's organization."); + } + const collection = new Collection(); + collection.id = model.id; + collection.organizationId = model.organizationId; + collection.readOnly = model.readOnly; + collection.name = await this.cryptoService.encrypt(model.name, key); + return collection; + } + + async decryptMany(collections: Collection[]): Promise { + if (collections == null) { + return []; + } + const decCollections: CollectionView[] = []; + const promises: Promise[] = []; + collections.forEach((collection) => { + promises.push(collection.decrypt().then((c) => decCollections.push(c))); + }); + await Promise.all(promises); + return decCollections.sort(Utils.getSortFunction(this.i18nService, "name")); + } + + async get(id: string): Promise { + const collections = await this.stateService.getEncryptedCollections(); + // eslint-disable-next-line + if (collections == null || !collections.hasOwnProperty(id)) { + return null; + } + + return new Collection(collections[id]); + } + + async getAll(): Promise { + const collections = await this.stateService.getEncryptedCollections(); + const response: Collection[] = []; + for (const id in collections) { + // eslint-disable-next-line + if (collections.hasOwnProperty(id)) { + response.push(new Collection(collections[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + let decryptedCollections = await this.stateService.getDecryptedCollections(); + if (decryptedCollections != null) { + return decryptedCollections; + } + + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const collections = await this.getAll(); + decryptedCollections = await this.decryptMany(collections); + await this.stateService.setDecryptedCollections(decryptedCollections); + return decryptedCollections; + } + + async getAllNested(collections: CollectionView[] = null): Promise[]> { + if (collections == null) { + collections = await this.getAllDecrypted(); + } + const nodes: TreeNode[] = []; + collections.forEach((c) => { + const collectionCopy = new CollectionView(); + collectionCopy.id = c.id; + collectionCopy.organizationId = c.organizationId; + const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter); + }); + return nodes; + } + + async getNested(id: string): Promise> { + const collections = await this.getAllNested(); + return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode; + } + + async upsert(collection: CollectionData | CollectionData[]): Promise { + let collections = await this.stateService.getEncryptedCollections(); + if (collections == null) { + collections = {}; + } + + if (collection instanceof CollectionData) { + const c = collection as CollectionData; + collections[c.id] = c; + } else { + (collection as CollectionData[]).forEach((c) => { + collections[c.id] = c; + }); + } + + await this.replace(collections); + } + + async replace(collections: { [id: string]: CollectionData }): Promise { + await this.clearCache(); + await this.stateService.setEncryptedCollections(collections); + } + + async clear(userId?: string): Promise { + await this.clearCache(userId); + await this.stateService.setEncryptedCollections(null, { userId: userId }); + } + + async delete(id: string | string[]): Promise { + const collections = await this.stateService.getEncryptedCollections(); + if (collections == null) { + return; + } + + if (typeof id === "string") { + delete collections[id]; + } else { + (id as string[]).forEach((i) => { + delete collections[i]; + }); + } + + await this.replace(collections); + } +} diff --git a/jslib/common/src/services/consoleLog.service.ts b/jslib/common/src/services/consoleLog.service.ts new file mode 100644 index 00000000..9959ef62 --- /dev/null +++ b/jslib/common/src/services/consoleLog.service.ts @@ -0,0 +1,72 @@ +import * as hrtime from "browser-hrtime"; + +import { LogService as LogServiceAbstraction } from "../abstractions/log.service"; +import { LogLevelType } from "../enums/logLevelType"; + +export class ConsoleLogService implements LogServiceAbstraction { + protected timersMap: Map = new Map(); + + constructor( + protected isDev: boolean, + protected filter: (level: LogLevelType) => boolean = null + ) {} + + debug(message: string) { + if (!this.isDev) { + return; + } + this.write(LogLevelType.Debug, message); + } + + info(message: string) { + this.write(LogLevelType.Info, message); + } + + warning(message: string) { + this.write(LogLevelType.Warning, message); + } + + error(message: string) { + this.write(LogLevelType.Error, message); + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + switch (level) { + case LogLevelType.Debug: + // eslint-disable-next-line + console.log(message); + break; + case LogLevelType.Info: + // eslint-disable-next-line + console.log(message); + break; + case LogLevelType.Warning: + // eslint-disable-next-line + console.warn(message); + break; + case LogLevelType.Error: + // eslint-disable-next-line + console.error(message); + break; + default: + break; + } + } + + time(label = "default") { + if (!this.timersMap.has(label)) { + this.timersMap.set(label, hrtime()); + } + } + + timeEnd(label = "default"): [number, number] { + const elapsed = hrtime(this.timersMap.get(label)); + this.timersMap.delete(label); + this.write(LogLevelType.Info, `${label}: ${elapsed[0] * 1000 + elapsed[1] / 10e6}ms`); + return elapsed; + } +} diff --git a/jslib/common/src/services/container.service.ts b/jslib/common/src/services/container.service.ts new file mode 100644 index 00000000..2880e7c7 --- /dev/null +++ b/jslib/common/src/services/container.service.ts @@ -0,0 +1,20 @@ +import { CryptoService } from "../abstractions/crypto.service"; + +export class ContainerService { + constructor(private cryptoService: CryptoService) {} + + // deprecated, use attachToGlobal instead + attachToWindow(win: any) { + this.attachToGlobal(win); + } + + attachToGlobal(global: any) { + if (!global.bitwardenContainerService) { + global.bitwardenContainerService = this; + } + } + + getCryptoService(): CryptoService { + return this.cryptoService; + } +} diff --git a/jslib/common/src/services/crypto.service.ts b/jslib/common/src/services/crypto.service.ts new file mode 100644 index 00000000..c49d7763 --- /dev/null +++ b/jslib/common/src/services/crypto.service.ts @@ -0,0 +1,969 @@ +import * as bigInt from "big-integer"; + +import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { LogService } from "../abstractions/log.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { EncryptionType } from "../enums/encryptionType"; +import { HashPurpose } from "../enums/hashPurpose"; +import { KdfType } from "../enums/kdfType"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { sequentialize } from "../misc/sequentialize"; +import { Utils } from "../misc/utils"; +import { EEFLongWordList } from "../misc/wordlist"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { EncryptedObject } from "../models/domain/encryptedObject"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse"; +import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse"; +import { ProfileProviderResponse } from "../models/response/profileProviderResponse"; + +export class CryptoService implements CryptoServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + protected platformUtilService: PlatformUtilsService, + protected logService: LogService, + protected stateService: StateService + ) {} + + async setKey(key: SymmetricCryptoKey, userId?: string): Promise { + await this.stateService.setCryptoMasterKey(key, { userId: userId }); + await this.storeKey(key, userId); + } + + async setKeyHash(keyHash: string): Promise { + await this.stateService.setKeyHash(keyHash); + } + + async setEncKey(encKey: string): Promise { + if (encKey == null) { + return; + } + + await this.stateService.setDecryptedCryptoSymmetricKey(null); + await this.stateService.setEncryptedCryptoSymmetricKey(encKey); + } + + async setEncPrivateKey(encPrivateKey: string): Promise { + if (encPrivateKey == null) { + return; + } + + await this.stateService.setDecryptedPrivateKey(null); + await this.stateService.setEncryptedPrivateKey(encPrivateKey); + } + + async setOrgKeys( + orgs: ProfileOrganizationResponse[], + providerOrgs: ProfileProviderOrganizationResponse[] + ): Promise { + const orgKeys: any = {}; + orgs.forEach((org) => { + orgKeys[org.id] = org.key; + }); + + for (const providerOrg of providerOrgs) { + // Convert provider encrypted keys to user encrypted. + const providerKey = await this.getProviderKey(providerOrg.providerId); + const decValue = await this.decryptToBytes(new EncString(providerOrg.key), providerKey); + orgKeys[providerOrg.id] = (await this.rsaEncrypt(decValue)).encryptedString; + } + + await this.stateService.setDecryptedOrganizationKeys(null); + return await this.stateService.setEncryptedOrganizationKeys(orgKeys); + } + + async setProviderKeys(providers: ProfileProviderResponse[]): Promise { + const providerKeys: any = {}; + providers.forEach((provider) => { + providerKeys[provider.id] = provider.key; + }); + + await this.stateService.setDecryptedProviderKeys(null); + return await this.stateService.setEncryptedProviderKeys(providerKeys); + } + + async getKey(keySuffix?: KeySuffixOptions, userId?: string): Promise { + const inMemoryKey = await this.stateService.getCryptoMasterKey({ userId: userId }); + + if (inMemoryKey != null) { + return inMemoryKey; + } + + keySuffix ||= KeySuffixOptions.Auto; + const symmetricKey = await this.getKeyFromStorage(keySuffix, userId); + + if (symmetricKey != null) { + // TODO: Refactor here so get key doesn't also set key + this.setKey(symmetricKey, userId); + } + + return symmetricKey; + } + + async getKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string + ): Promise { + const key = await this.retrieveKeyFromStorage(keySuffix, userId); + if (key != null) { + const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer); + + if (!(await this.validateKey(symmetricKey))) { + this.logService.warning("Wrong key, throwing away stored key"); + await this.clearSecretKeyStore(userId); + return null; + } + + return symmetricKey; + } + return null; + } + + async getKeyHash(): Promise { + return await this.stateService.getKeyHash(); + } + + async compareAndUpdateKeyHash(masterPassword: string, key: SymmetricCryptoKey): Promise { + const storedKeyHash = await this.getKeyHash(); + if (masterPassword != null && storedKeyHash != null) { + const localKeyHash = await this.hashPassword( + masterPassword, + key, + HashPurpose.LocalAuthorization + ); + if (localKeyHash != null && storedKeyHash === localKeyHash) { + return true; + } + + // TODO: remove serverKeyHash check in 1-2 releases after everyone's keyHash has been updated + const serverKeyHash = await this.hashPassword( + masterPassword, + key, + HashPurpose.ServerAuthorization + ); + if (serverKeyHash != null && storedKeyHash === serverKeyHash) { + await this.setKeyHash(localKeyHash); + return true; + } + } + + return false; + } + + @sequentialize(() => "getEncKey") + getEncKey(key: SymmetricCryptoKey = null): Promise { + return this.getEncKeyHelper(key); + } + + async getPublicKey(): Promise { + const inMemoryPublicKey = await this.stateService.getPublicKey(); + if (inMemoryPublicKey != null) { + return inMemoryPublicKey; + } + + const privateKey = await this.getPrivateKey(); + if (privateKey == null) { + return null; + } + + const publicKey = await this.cryptoFunctionService.rsaExtractPublicKey(privateKey); + await this.stateService.setPublicKey(publicKey); + return publicKey; + } + + async getPrivateKey(): Promise { + const decryptedPrivateKey = await this.stateService.getDecryptedPrivateKey(); + if (decryptedPrivateKey != null) { + return decryptedPrivateKey; + } + + const encPrivateKey = await this.stateService.getEncryptedPrivateKey(); + if (encPrivateKey == null) { + return null; + } + + const privateKey = await this.decryptToBytes(new EncString(encPrivateKey), null); + await this.stateService.setDecryptedPrivateKey(privateKey); + return privateKey; + } + + async getFingerprint(userId: string, publicKey?: ArrayBuffer): Promise { + if (publicKey == null) { + publicKey = await this.getPublicKey(); + } + if (publicKey === null) { + throw new Error("No public key available."); + } + const keyFingerprint = await this.cryptoFunctionService.hash(publicKey, "sha256"); + const userFingerprint = await this.cryptoFunctionService.hkdfExpand( + keyFingerprint, + userId, + 32, + "sha256" + ); + return this.hashPhrase(userFingerprint); + } + + @sequentialize(() => "getOrgKeys") + async getOrgKeys(): Promise> { + const orgKeys: Map = new Map(); + const decryptedOrganizationKeys = await this.stateService.getDecryptedOrganizationKeys(); + if (decryptedOrganizationKeys != null && decryptedOrganizationKeys.size > 0) { + return decryptedOrganizationKeys; + } + + const encOrgKeys = await this.stateService.getEncryptedOrganizationKeys(); + if (encOrgKeys == null) { + return null; + } + + let setKey = false; + + for (const orgId in encOrgKeys) { + // eslint-disable-next-line + if (!encOrgKeys.hasOwnProperty(orgId)) { + continue; + } + + const decValue = await this.rsaDecrypt(encOrgKeys[orgId]); + orgKeys.set(orgId, new SymmetricCryptoKey(decValue)); + setKey = true; + } + + if (setKey) { + await this.stateService.setDecryptedOrganizationKeys(orgKeys); + } + + return orgKeys; + } + + async getOrgKey(orgId: string): Promise { + if (orgId == null) { + return null; + } + + const orgKeys = await this.getOrgKeys(); + if (orgKeys == null || !orgKeys.has(orgId)) { + return null; + } + + return orgKeys.get(orgId); + } + + @sequentialize(() => "getProviderKeys") + async getProviderKeys(): Promise> { + const providerKeys: Map = new Map(); + const decryptedProviderKeys = await this.stateService.getDecryptedProviderKeys(); + if (decryptedProviderKeys != null && decryptedProviderKeys.size > 0) { + return decryptedProviderKeys; + } + + const encProviderKeys = await this.stateService.getEncryptedProviderKeys(); + if (encProviderKeys == null) { + return null; + } + + let setKey = false; + + for (const orgId in encProviderKeys) { + // eslint-disable-next-line + if (!encProviderKeys.hasOwnProperty(orgId)) { + continue; + } + + const decValue = await this.rsaDecrypt(encProviderKeys[orgId]); + providerKeys.set(orgId, new SymmetricCryptoKey(decValue)); + setKey = true; + } + + if (setKey) { + await this.stateService.setDecryptedProviderKeys(providerKeys); + } + + return providerKeys; + } + + async getProviderKey(providerId: string): Promise { + if (providerId == null) { + return null; + } + + const providerKeys = await this.getProviderKeys(); + if (providerKeys == null || !providerKeys.has(providerId)) { + return null; + } + + return providerKeys.get(providerId); + } + + async hasKey(): Promise { + return ( + (await this.hasKeyInMemory()) || + (await this.hasKeyStored(KeySuffixOptions.Auto)) || + (await this.hasKeyStored(KeySuffixOptions.Biometric)) + ); + } + + async hasKeyInMemory(userId?: string): Promise { + return (await this.stateService.getCryptoMasterKey({ userId: userId })) != null; + } + + async hasKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + switch (keySuffix) { + case KeySuffixOptions.Auto: + return (await this.stateService.getCryptoMasterKeyAuto({ userId: userId })) != null; + case KeySuffixOptions.Biometric: + return (await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId })) === true; + default: + return false; + } + } + + async hasEncKey(): Promise { + return (await this.stateService.getEncryptedCryptoSymmetricKey()) != null; + } + + async clearKey(clearSecretStorage = true, userId?: string): Promise { + await this.stateService.setCryptoMasterKey(null, { userId: userId }); + await this.stateService.setLegacyEtmKey(null, { userId: userId }); + if (clearSecretStorage) { + await this.clearSecretKeyStore(userId); + } + } + + async clearStoredKey(keySuffix: KeySuffixOptions) { + keySuffix === KeySuffixOptions.Auto + ? await this.stateService.setCryptoMasterKeyAuto(null) + : await this.stateService.setCryptoMasterKeyBiometric(null); + } + + async clearKeyHash(userId?: string): Promise { + return await this.stateService.setKeyHash(null, { userId: userId }); + } + + async clearEncKey(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedCryptoSymmetricKey(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedCryptoSymmetricKey(null, { userId: userId }); + } + } + + async clearKeyPair(memoryOnly?: boolean, userId?: string): Promise { + const keysToClear: Promise[] = [ + this.stateService.setDecryptedPrivateKey(null, { userId: userId }), + this.stateService.setPublicKey(null, { userId: userId }), + ]; + if (!memoryOnly) { + keysToClear.push(this.stateService.setEncryptedPrivateKey(null, { userId: userId })); + } + return Promise.all(keysToClear); + } + + async clearOrgKeys(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedOrganizationKeys(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedOrganizationKeys(null, { userId: userId }); + } + } + + async clearProviderKeys(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedProviderKeys(null, { userId: userId }); + if (!memoryOnly) { + await this.stateService.setEncryptedProviderKeys(null, { userId: userId }); + } + } + + async clearPinProtectedKey(userId?: string): Promise { + return await this.stateService.setEncryptedPinProtected(null, { userId: userId }); + } + + async clearKeys(userId?: string): Promise { + await this.clearKey(true, userId); + await this.clearKeyHash(userId); + await this.clearOrgKeys(false, userId); + await this.clearProviderKeys(false, userId); + await this.clearEncKey(false, userId); + await this.clearKeyPair(false, userId); + await this.clearPinProtectedKey(userId); + } + + async toggleKey(): Promise { + const key = await this.getKey(); + + await this.setKey(key); + } + + async makeKey( + password: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ): Promise { + let key: ArrayBuffer = null; + if (kdf == null || kdf === KdfType.PBKDF2_SHA256) { + if (kdfIterations == null) { + kdfIterations = 5000; + } else if (kdfIterations < 5000) { + throw new Error("PBKDF2 iteration minimum is 5000."); + } + key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfIterations); + } else { + throw new Error("Unknown Kdf."); + } + return new SymmetricCryptoKey(key); + } + + async makeKeyFromPin( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number, + protectedKeyCs: EncString = null + ): Promise { + if (protectedKeyCs == null) { + const pinProtectedKey = await this.stateService.getEncryptedPinProtected(); + if (pinProtectedKey == null) { + throw new Error("No PIN protected key found."); + } + protectedKeyCs = new EncString(pinProtectedKey); + } + const pinKey = await this.makePinKey(pin, salt, kdf, kdfIterations); + const decKey = await this.decryptToBytes(protectedKeyCs, pinKey); + return new SymmetricCryptoKey(decKey); + } + + async makeShareKey(): Promise<[EncString, SymmetricCryptoKey]> { + const shareKey = await this.cryptoFunctionService.randomBytes(64); + const publicKey = await this.getPublicKey(); + const encShareKey = await this.rsaEncrypt(shareKey, publicKey); + return [encShareKey, new SymmetricCryptoKey(shareKey)]; + } + + async makeKeyPair(key?: SymmetricCryptoKey): Promise<[string, EncString]> { + const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); + const publicB64 = Utils.fromBufferToB64(keyPair[0]); + const privateEnc = await this.encrypt(keyPair[1], key); + return [publicB64, privateEnc]; + } + + async makePinKey( + pin: string, + salt: string, + kdf: KdfType, + kdfIterations: number + ): Promise { + const pinKey = await this.makeKey(pin, salt, kdf, kdfIterations); + return await this.stretchKey(pinKey); + } + + async makeSendKey(keyMaterial: ArrayBuffer): Promise { + const sendKey = await this.cryptoFunctionService.hkdf( + keyMaterial, + "bitwarden-send", + "send", + 64, + "sha256" + ); + return new SymmetricCryptoKey(sendKey); + } + + async hashPassword( + password: string, + key: SymmetricCryptoKey, + hashPurpose?: HashPurpose + ): Promise { + if (key == null) { + key = await this.getKey(); + } + if (password == null || key == null) { + throw new Error("Invalid parameters."); + } + + const iterations = hashPurpose === HashPurpose.LocalAuthorization ? 2 : 1; + const hash = await this.cryptoFunctionService.pbkdf2(key.key, password, "sha256", iterations); + return Utils.fromBufferToB64(hash); + } + + async makeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, EncString]> { + const theKey = await this.getKeyForEncryption(key); + const encKey = await this.cryptoFunctionService.randomBytes(64); + return this.buildEncKey(theKey, encKey); + } + + async remakeEncKey( + key: SymmetricCryptoKey, + encKey?: SymmetricCryptoKey + ): Promise<[SymmetricCryptoKey, EncString]> { + if (encKey == null) { + encKey = await this.getEncKey(); + } + return this.buildEncKey(key, encKey.key); + } + + async encrypt(plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey): Promise { + if (plainValue == null) { + return Promise.resolve(null); + } + + let plainBuf: ArrayBuffer; + if (typeof plainValue === "string") { + plainBuf = Utils.fromUtf8ToArray(plainValue).buffer; + } else { + plainBuf = plainValue; + } + + const encObj = await this.aesEncrypt(plainBuf, key); + const iv = Utils.fromBufferToB64(encObj.iv); + const data = Utils.fromBufferToB64(encObj.data); + const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null; + return new EncString(encObj.key.encType, data, iv, mac); + } + + async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise { + const encValue = await this.aesEncrypt(plainValue, key); + let macLen = 0; + if (encValue.mac != null) { + macLen = encValue.mac.byteLength; + } + + const encBytes = new Uint8Array(1 + encValue.iv.byteLength + macLen + encValue.data.byteLength); + encBytes.set([encValue.key.encType]); + encBytes.set(new Uint8Array(encValue.iv), 1); + if (encValue.mac != null) { + encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength); + } + + encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen); + return new EncArrayBuffer(encBytes.buffer); + } + + async rsaEncrypt(data: ArrayBuffer, publicKey?: ArrayBuffer): Promise { + if (publicKey == null) { + publicKey = await this.getPublicKey(); + } + if (publicKey == null) { + throw new Error("Public key unavailable."); + } + + const encBytes = await this.cryptoFunctionService.rsaEncrypt(data, publicKey, "sha1"); + return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes)); + } + + async rsaDecrypt(encValue: string, privateKeyValue?: ArrayBuffer): Promise { + const headerPieces = encValue.split("."); + let encType: EncryptionType = null; + let encPieces: string[]; + + if (headerPieces.length === 1) { + encType = EncryptionType.Rsa2048_OaepSha256_B64; + encPieces = [headerPieces[0]]; + } else if (headerPieces.length === 2) { + try { + encType = parseInt(headerPieces[0], null); + encPieces = headerPieces[1].split("|"); + } catch (e) { + this.logService.error(e); + } + } + + switch (encType) { + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha1_B64: + case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: // HmacSha256 types are deprecated + case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: + break; + default: + throw new Error("encType unavailable."); + } + + if (encPieces == null || encPieces.length <= 0) { + throw new Error("encPieces unavailable."); + } + + const data = Utils.fromB64ToArray(encPieces[0]).buffer; + const privateKey = privateKeyValue ?? (await this.getPrivateKey()); + if (privateKey == null) { + throw new Error("No private key."); + } + + let alg: "sha1" | "sha256" = "sha1"; + switch (encType) { + case EncryptionType.Rsa2048_OaepSha256_B64: + case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: + alg = "sha256"; + break; + case EncryptionType.Rsa2048_OaepSha1_B64: + case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: + break; + default: + throw new Error("encType unavailable."); + } + + return this.cryptoFunctionService.rsaDecrypt(data, privateKey, alg); + } + + async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise { + const iv = Utils.fromB64ToArray(encString.iv).buffer; + const data = Utils.fromB64ToArray(encString.data).buffer; + const mac = encString.mac ? Utils.fromB64ToArray(encString.mac).buffer : null; + const decipher = await this.aesDecryptToBytes(encString.encryptionType, data, iv, mac, key); + if (decipher == null) { + return null; + } + + return decipher; + } + + async decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise { + return await this.aesDecryptToUtf8( + encString.encryptionType, + encString.data, + encString.iv, + encString.mac, + key + ); + } + + async decryptFromBytes(encBuf: ArrayBuffer, key: SymmetricCryptoKey): Promise { + if (encBuf == null) { + throw new Error("no encBuf."); + } + + const encBytes = new Uint8Array(encBuf); + const encType = encBytes[0]; + let ctBytes: Uint8Array = null; + let ivBytes: Uint8Array = null; + let macBytes: Uint8Array = null; + + switch (encType) { + case EncryptionType.AesCbc128_HmacSha256_B64: + case EncryptionType.AesCbc256_HmacSha256_B64: + if (encBytes.length <= 49) { + // 1 + 16 + 32 + ctLength + return null; + } + + ivBytes = encBytes.slice(1, 17); + macBytes = encBytes.slice(17, 49); + ctBytes = encBytes.slice(49); + break; + case EncryptionType.AesCbc256_B64: + if (encBytes.length <= 17) { + // 1 + 16 + ctLength + return null; + } + + ivBytes = encBytes.slice(1, 17); + ctBytes = encBytes.slice(17); + break; + default: + return null; + } + + return await this.aesDecryptToBytes( + encType, + ctBytes.buffer, + ivBytes.buffer, + macBytes != null ? macBytes.buffer : null, + key + ); + } + + // EFForg/OpenWireless + // ref https://github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js + async randomNumber(min: number, max: number): Promise { + let rval = 0; + const range = max - min + 1; + const bitsNeeded = Math.ceil(Math.log2(range)); + if (bitsNeeded > 53) { + throw new Error("We cannot generate numbers larger than 53 bits."); + } + + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const mask = Math.pow(2, bitsNeeded) - 1; + // 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111 + + // Fill a byte array with N random numbers + const byteArray = new Uint8Array(await this.cryptoFunctionService.randomBytes(bytesNeeded)); + + let p = (bytesNeeded - 1) * 8; + for (let i = 0; i < bytesNeeded; i++) { + rval += byteArray[i] * Math.pow(2, p); + p -= 8; + } + + // Use & to apply the mask and reduce the number of recursive lookups + rval = rval & mask; + + if (rval >= range) { + // Integer out of acceptable range + return this.randomNumber(min, max); + } + + // Return an integer that falls within the range + return min + rval; + } + + async validateKey(key: SymmetricCryptoKey) { + try { + const encPrivateKey = await this.stateService.getEncryptedPrivateKey(); + const encKey = await this.getEncKeyHelper(key); + if (encPrivateKey == null || encKey == null) { + return false; + } + + const privateKey = await this.decryptToBytes(new EncString(encPrivateKey), encKey); + await this.cryptoFunctionService.rsaExtractPublicKey(privateKey); + } catch (e) { + return false; + } + + return true; + } + + // Helpers + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) { + await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId }); + } else if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId }); + } else { + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + } + + protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string) { + let shouldStoreKey = false; + if (keySuffix === KeySuffixOptions.Auto) { + const vaultTimeout = await this.stateService.getVaultTimeout({ userId: userId }); + shouldStoreKey = vaultTimeout == null; + } else if (keySuffix === KeySuffixOptions.Biometric) { + const biometricUnlock = await this.stateService.getBiometricUnlock({ userId: userId }); + shouldStoreKey = biometricUnlock && this.platformUtilService.supportsSecureStorage(); + } + return shouldStoreKey; + } + + protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string) { + return keySuffix === KeySuffixOptions.Auto + ? await this.stateService.getCryptoMasterKeyAuto({ userId: userId }) + : await this.stateService.getCryptoMasterKeyBiometric({ userId: userId }); + } + + private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise { + const obj = new EncryptedObject(); + obj.key = await this.getKeyForEncryption(key); + obj.iv = await this.cryptoFunctionService.randomBytes(16); + obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, obj.key.encKey); + + if (obj.key.macKey != null) { + const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength); + macData.set(new Uint8Array(obj.iv), 0); + macData.set(new Uint8Array(obj.data), obj.iv.byteLength); + obj.mac = await this.cryptoFunctionService.hmac(macData.buffer, obj.key.macKey, "sha256"); + } + + return obj; + } + + private async aesDecryptToUtf8( + encType: EncryptionType, + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): Promise { + const keyForEnc = await this.getKeyForEncryption(key); + const theKey = await this.resolveLegacyKey(encType, keyForEnc); + + if (theKey.macKey != null && mac == null) { + this.logService.error("mac required."); + return null; + } + + if (theKey.encType !== encType) { + this.logService.error("encType unavailable."); + return null; + } + + const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(data, iv, mac, theKey); + if (fastParams.macKey != null && fastParams.mac != null) { + const computedMac = await this.cryptoFunctionService.hmacFast( + fastParams.macData, + fastParams.macKey, + "sha256" + ); + const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac); + if (!macsEqual) { + this.logService.error("mac failed."); + return null; + } + } + + return this.cryptoFunctionService.aesDecryptFast(fastParams); + } + + private async aesDecryptToBytes( + encType: EncryptionType, + data: ArrayBuffer, + iv: ArrayBuffer, + mac: ArrayBuffer, + key: SymmetricCryptoKey + ): Promise { + const keyForEnc = await this.getKeyForEncryption(key); + const theKey = await this.resolveLegacyKey(encType, keyForEnc); + + if (theKey.macKey != null && mac == null) { + return null; + } + + if (theKey.encType !== encType) { + return null; + } + + if (theKey.macKey != null && mac != null) { + const macData = new Uint8Array(iv.byteLength + data.byteLength); + macData.set(new Uint8Array(iv), 0); + macData.set(new Uint8Array(data), iv.byteLength); + const computedMac = await this.cryptoFunctionService.hmac( + macData.buffer, + theKey.macKey, + "sha256" + ); + if (computedMac === null) { + return null; + } + + const macsMatch = await this.cryptoFunctionService.compare(mac, computedMac); + if (!macsMatch) { + this.logService.error("mac failed."); + return null; + } + } + + return await this.cryptoFunctionService.aesDecrypt(data, iv, theKey.encKey); + } + + private async getKeyForEncryption(key?: SymmetricCryptoKey): Promise { + if (key != null) { + return key; + } + + const encKey = await this.getEncKey(); + if (encKey != null) { + return encKey; + } + + return await this.getKey(); + } + + private async resolveLegacyKey( + encType: EncryptionType, + key: SymmetricCryptoKey + ): Promise { + if ( + encType === EncryptionType.AesCbc128_HmacSha256_B64 && + key.encType === EncryptionType.AesCbc256_B64 + ) { + // Old encrypt-then-mac scheme, make a new key + let legacyKey = await this.stateService.getLegacyEtmKey(); + if (legacyKey == null) { + legacyKey = new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64); + await this.stateService.setLegacyEtmKey(legacyKey); + } + return legacyKey; + } + + return key; + } + + private async stretchKey(key: SymmetricCryptoKey): Promise { + const newKey = new Uint8Array(64); + const encKey = await this.cryptoFunctionService.hkdfExpand(key.key, "enc", 32, "sha256"); + const macKey = await this.cryptoFunctionService.hkdfExpand(key.key, "mac", 32, "sha256"); + newKey.set(new Uint8Array(encKey)); + newKey.set(new Uint8Array(macKey), 32); + return new SymmetricCryptoKey(newKey.buffer); + } + + private async hashPhrase(hash: ArrayBuffer, minimumEntropy = 64) { + const entropyPerWord = Math.log(EEFLongWordList.length) / Math.log(2); + let numWords = Math.ceil(minimumEntropy / entropyPerWord); + + const hashArr = Array.from(new Uint8Array(hash)); + const entropyAvailable = hashArr.length * 4; + if (numWords * entropyPerWord > entropyAvailable) { + throw new Error("Output entropy of hash function is too small"); + } + + const phrase: string[] = []; + let hashNumber = bigInt.fromArray(hashArr, 256); + while (numWords--) { + const remainder = hashNumber.mod(EEFLongWordList.length); + hashNumber = hashNumber.divide(EEFLongWordList.length); + phrase.push(EEFLongWordList[remainder as any]); + } + return phrase; + } + + private async buildEncKey( + key: SymmetricCryptoKey, + encKey: ArrayBuffer + ): Promise<[SymmetricCryptoKey, EncString]> { + let encKeyEnc: EncString = null; + if (key.key.byteLength === 32) { + const newKey = await this.stretchKey(key); + encKeyEnc = await this.encrypt(encKey, newKey); + } else if (key.key.byteLength === 64) { + encKeyEnc = await this.encrypt(encKey, key); + } else { + throw new Error("Invalid key size."); + } + return [new SymmetricCryptoKey(encKey), encKeyEnc]; + } + + private async clearSecretKeyStore(userId?: string): Promise { + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + + private async getEncKeyHelper(key: SymmetricCryptoKey = null): Promise { + const inMemoryKey = await this.stateService.getDecryptedCryptoSymmetricKey(); + if (inMemoryKey != null) { + return inMemoryKey; + } + + const encKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + if (encKey == null) { + return null; + } + + if (key == null) { + key = await this.getKey(); + } + if (key == null) { + return null; + } + + let decEncKey: ArrayBuffer; + const encKeyCipher = new EncString(encKey); + if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_B64) { + decEncKey = await this.decryptToBytes(encKeyCipher, key); + } else if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_HmacSha256_B64) { + const newKey = await this.stretchKey(key); + decEncKey = await this.decryptToBytes(encKeyCipher, newKey); + } else { + throw new Error("Unsupported encKey type."); + } + if (decEncKey == null) { + return null; + } + const symmetricCryptoKey = new SymmetricCryptoKey(decEncKey); + await this.stateService.setDecryptedCryptoSymmetricKey(symmetricCryptoKey); + return symmetricCryptoKey; + } +} diff --git a/jslib/common/src/services/environment.service.ts b/jslib/common/src/services/environment.service.ts new file mode 100644 index 00000000..b13afd59 --- /dev/null +++ b/jslib/common/src/services/environment.service.ts @@ -0,0 +1,188 @@ +import { Observable, Subject } from "rxjs"; + +import { + EnvironmentService as EnvironmentServiceAbstraction, + Urls, +} from "../abstractions/environment.service"; +import { StateService } from "../abstractions/state.service"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; + +export class EnvironmentService implements EnvironmentServiceAbstraction { + private readonly urlsSubject = new Subject(); + urls: Observable = this.urlsSubject; + + private baseUrl: string; + private webVaultUrl: string; + private apiUrl: string; + private identityUrl: string; + private iconsUrl: string; + private notificationsUrl: string; + private eventsUrl: string; + private keyConnectorUrl: string; + + constructor(private stateService: StateService) { + this.stateService.activeAccount.subscribe(async () => { + await this.setUrlsFromStorage(); + }); + } + + hasBaseUrl() { + return this.baseUrl != null; + } + + getNotificationsUrl() { + if (this.notificationsUrl != null) { + return this.notificationsUrl; + } + + if (this.baseUrl != null) { + return this.baseUrl + "/notifications"; + } + + return "https://notifications.bitwarden.com"; + } + + getWebVaultUrl() { + if (this.webVaultUrl != null) { + return this.webVaultUrl; + } + + if (this.baseUrl) { + return this.baseUrl; + } + return "https://vault.bitwarden.com"; + } + + getSendUrl() { + return this.getWebVaultUrl() === "https://vault.bitwarden.com" + ? "https://send.bitwarden.com/#" + : this.getWebVaultUrl() + "/#/send/"; + } + + getIconsUrl() { + if (this.iconsUrl != null) { + return this.iconsUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/icons"; + } + + return "https://icons.bitwarden.net"; + } + + getApiUrl() { + if (this.apiUrl != null) { + return this.apiUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/api"; + } + + return "https://api.bitwarden.com"; + } + + getIdentityUrl() { + if (this.identityUrl != null) { + return this.identityUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/identity"; + } + + return "https://identity.bitwarden.com"; + } + + getEventsUrl() { + if (this.eventsUrl != null) { + return this.eventsUrl; + } + + if (this.baseUrl) { + return this.baseUrl + "/events"; + } + + return "https://events.bitwarden.com"; + } + + getKeyConnectorUrl() { + return this.keyConnectorUrl; + } + + async setUrlsFromStorage(): Promise { + const urls: any = await this.stateService.getEnvironmentUrls(); + const envUrls = new EnvironmentUrls(); + + this.baseUrl = envUrls.base = urls.base; + this.webVaultUrl = urls.webVault; + this.apiUrl = envUrls.api = urls.api; + this.identityUrl = envUrls.identity = urls.identity; + this.iconsUrl = urls.icons; + this.notificationsUrl = urls.notifications; + this.eventsUrl = envUrls.events = urls.events; + this.keyConnectorUrl = urls.keyConnector; + } + + async setUrls(urls: Urls): Promise { + urls.base = this.formatUrl(urls.base); + urls.webVault = this.formatUrl(urls.webVault); + urls.api = this.formatUrl(urls.api); + urls.identity = this.formatUrl(urls.identity); + urls.icons = this.formatUrl(urls.icons); + urls.notifications = this.formatUrl(urls.notifications); + urls.events = this.formatUrl(urls.events); + urls.keyConnector = this.formatUrl(urls.keyConnector); + + await this.stateService.setEnvironmentUrls({ + base: urls.base, + api: urls.api, + identity: urls.identity, + webVault: urls.webVault, + icons: urls.icons, + notifications: urls.notifications, + events: urls.events, + keyConnector: urls.keyConnector, + }); + + this.baseUrl = urls.base; + this.webVaultUrl = urls.webVault; + this.apiUrl = urls.api; + this.identityUrl = urls.identity; + this.iconsUrl = urls.icons; + this.notificationsUrl = urls.notifications; + this.eventsUrl = urls.events; + this.keyConnectorUrl = urls.keyConnector; + + this.urlsSubject.next(urls); + + return urls; + } + + getUrls() { + return { + base: this.baseUrl, + webVault: this.webVaultUrl, + api: this.apiUrl, + identity: this.identityUrl, + icons: this.iconsUrl, + notifications: this.notificationsUrl, + events: this.eventsUrl, + keyConnector: this.keyConnectorUrl, + }; + } + + private formatUrl(url: string): string { + if (url == null || url === "") { + return null; + } + + url = url.replace(/\/+$/g, ""); + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "https://" + url; + } + + return url.trim(); + } +} diff --git a/jslib/common/src/services/event.service.ts b/jslib/common/src/services/event.service.ts new file mode 100644 index 00000000..c0024d49 --- /dev/null +++ b/jslib/common/src/services/event.service.ts @@ -0,0 +1,99 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { EventService as EventServiceAbstraction } from "../abstractions/event.service"; +import { LogService } from "../abstractions/log.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { EventType } from "../enums/eventType"; +import { EventData } from "../models/data/eventData"; +import { EventRequest } from "../models/request/eventRequest"; + +export class EventService implements EventServiceAbstraction { + private inited = false; + + constructor( + private apiService: ApiService, + private cipherService: CipherService, + private stateService: StateService, + private logService: LogService, + private organizationService: OrganizationService + ) {} + + init(checkOnInterval: boolean) { + if (this.inited) { + return; + } + + this.inited = true; + if (checkOnInterval) { + this.uploadEvents(); + setInterval(() => this.uploadEvents(), 60 * 1000); // check every 60 seconds + } + } + + async collect( + eventType: EventType, + cipherId: string = null, + uploadImmediately = false + ): Promise { + const authed = await this.stateService.getIsAuthenticated(); + if (!authed) { + return; + } + const organizations = await this.organizationService.getAll(); + if (organizations == null) { + return; + } + const orgIds = new Set(organizations.filter((o) => o.useEvents).map((o) => o.id)); + if (orgIds.size === 0) { + return; + } + if (cipherId != null) { + const cipher = await this.cipherService.get(cipherId); + if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) { + return; + } + } + let eventCollection = await this.stateService.getEventCollection(); + if (eventCollection == null) { + eventCollection = []; + } + const event = new EventData(); + event.type = eventType; + event.cipherId = cipherId; + event.date = new Date().toISOString(); + eventCollection.push(event); + await this.stateService.setEventCollection(eventCollection); + if (uploadImmediately) { + await this.uploadEvents(); + } + } + + async uploadEvents(userId?: string): Promise { + const authed = await this.stateService.getIsAuthenticated({ userId: userId }); + if (!authed) { + return; + } + const eventCollection = await this.stateService.getEventCollection({ userId: userId }); + if (eventCollection == null || eventCollection.length === 0) { + return; + } + const request = eventCollection.map((e) => { + const req = new EventRequest(); + req.type = e.type; + req.cipherId = e.cipherId; + req.date = e.date; + return req; + }); + try { + await this.apiService.postEventsCollect(request); + this.clearEvents(userId); + } catch (e) { + this.logService.error(e); + } + } + + async clearEvents(userId?: string): Promise { + await this.stateService.setEventCollection(null, { userId: userId }); + } +} diff --git a/jslib/common/src/services/export.service.ts b/jslib/common/src/services/export.service.ts new file mode 100644 index 00000000..55f9206f --- /dev/null +++ b/jslib/common/src/services/export.service.ts @@ -0,0 +1,444 @@ +import * as papa from "papaparse"; + +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { + ExportFormat, + ExportService as ExportServiceAbstraction, +} from "../abstractions/export.service"; +import { FolderService } from "../abstractions/folder.service"; +import { CipherType } from "../enums/cipherType"; +import { DEFAULT_KDF_ITERATIONS, KdfType } from "../enums/kdfType"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { Cipher } from "../models/domain/cipher"; +import { Collection } from "../models/domain/collection"; +import { Folder } from "../models/domain/folder"; +import { CipherWithIds as CipherExport } from "../models/export/cipherWithIds"; +import { CollectionWithId as CollectionExport } from "../models/export/collectionWithId"; +import { Event } from "../models/export/event"; +import { FolderWithId as FolderExport } from "../models/export/folderWithId"; +import { CollectionDetailsResponse } from "../models/response/collectionResponse"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { EventView } from "../models/view/eventView"; +import { FolderView } from "../models/view/folderView"; + +export class ExportService implements ExportServiceAbstraction { + constructor( + private folderService: FolderService, + private cipherService: CipherService, + private apiService: ApiService, + private cryptoService: CryptoService, + private cryptoFunctionService: CryptoFunctionService + ) {} + + async getExport(format: ExportFormat = "csv", organizationId?: string): Promise { + if (organizationId) { + return await this.getOrganizationExport(organizationId, format); + } + + if (format === "encrypted_json") { + return this.getEncryptedExport(); + } else { + return this.getDecryptedExport(format); + } + } + + async getPasswordProtectedExport(password: string, organizationId?: string): Promise { + const clearText = organizationId + ? await this.getOrganizationExport(organizationId, "json") + : await this.getExport("json"); + + const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16)); + const kdfIterations = DEFAULT_KDF_ITERATIONS; + const key = await this.cryptoService.makePinKey( + password, + salt, + KdfType.PBKDF2_SHA256, + kdfIterations + ); + + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key); + const encText = await this.cryptoService.encrypt(clearText, key); + + const jsonDoc: any = { + encrypted: true, + passwordProtected: true, + salt: salt, + kdfIterations: kdfIterations, + kdfType: KdfType.PBKDF2_SHA256, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + data: encText.encryptedString, + }; + + return JSON.stringify(jsonDoc, null, " "); + } + + async getOrganizationExport( + organizationId: string, + format: ExportFormat = "csv" + ): Promise { + if (format === "encrypted_json") { + return this.getOrganizationEncryptedExport(organizationId); + } else { + return this.getOrganizationDecryptedExport(organizationId, format); + } + } + + async getEventExport(events: EventView[]): Promise { + return papa.unparse(events.map((e) => new Event(e))); + } + + getFileName(prefix: string = null, extension = "csv"): string { + const now = new Date(); + const dateString = + now.getFullYear() + + "" + + this.padNumber(now.getMonth() + 1, 2) + + "" + + this.padNumber(now.getDate(), 2) + + this.padNumber(now.getHours(), 2) + + "" + + this.padNumber(now.getMinutes(), 2) + + this.padNumber(now.getSeconds(), 2); + + return "bitwarden" + (prefix ? "_" + prefix : "") + "_export_" + dateString + "." + extension; + } + + private async getDecryptedExport(format: "json" | "csv"): Promise { + let decFolders: FolderView[] = []; + let decCiphers: CipherView[] = []; + const promises = []; + + promises.push( + this.folderService.getAllDecrypted().then((folders) => { + decFolders = folders; + }) + ); + + promises.push( + this.cipherService.getAllDecrypted().then((ciphers) => { + decCiphers = ciphers.filter((f) => f.deletedDate == null); + }) + ); + + await Promise.all(promises); + + if (format === "csv") { + const foldersMap = new Map(); + decFolders.forEach((f) => { + if (f.id != null) { + foldersMap.set(f.id, f); + } + }); + + const exportCiphers: any[] = []; + decCiphers.forEach((c) => { + // only export logins and secure notes + if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { + return; + } + if (c.organizationId != null) { + return; + } + + const cipher: any = {}; + cipher.folder = + c.folderId != null && foldersMap.has(c.folderId) ? foldersMap.get(c.folderId).name : null; + cipher.favorite = c.favorite ? 1 : null; + this.buildCommonCipher(cipher, c); + exportCiphers.push(cipher); + }); + + return papa.unparse(exportCiphers); + } else { + const jsonDoc: any = { + encrypted: false, + folders: [], + items: [], + }; + + decFolders.forEach((f) => { + if (f.id == null) { + return; + } + const folder = new FolderExport(); + folder.build(f); + jsonDoc.folders.push(folder); + }); + + decCiphers.forEach((c) => { + if (c.organizationId != null) { + return; + } + const cipher = new CipherExport(); + cipher.build(c); + cipher.collectionIds = null; + jsonDoc.items.push(cipher); + }); + + return JSON.stringify(jsonDoc, null, " "); + } + } + + private async getEncryptedExport(): Promise { + let folders: Folder[] = []; + let ciphers: Cipher[] = []; + const promises = []; + + promises.push( + this.folderService.getAll().then((f) => { + folders = f; + }) + ); + + promises.push( + this.cipherService.getAll().then((c) => { + ciphers = c.filter((f) => f.deletedDate == null); + }) + ); + + await Promise.all(promises); + + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid()); + + const jsonDoc: any = { + encrypted: true, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + folders: [], + items: [], + }; + + folders.forEach((f) => { + if (f.id == null) { + return; + } + const folder = new FolderExport(); + folder.build(f); + jsonDoc.folders.push(folder); + }); + + ciphers.forEach((c) => { + if (c.organizationId != null) { + return; + } + const cipher = new CipherExport(); + cipher.build(c); + cipher.collectionIds = null; + jsonDoc.items.push(cipher); + }); + + return JSON.stringify(jsonDoc, null, " "); + } + + private async getOrganizationDecryptedExport( + organizationId: string, + format: "json" | "csv" + ): Promise { + const decCollections: CollectionView[] = []; + const decCiphers: CipherView[] = []; + const promises = []; + + promises.push( + this.apiService.getCollections(organizationId).then((collections) => { + const collectionPromises: any = []; + if (collections != null && collections.data != null && collections.data.length > 0) { + collections.data.forEach((c) => { + const collection = new Collection(new CollectionData(c as CollectionDetailsResponse)); + collectionPromises.push( + collection.decrypt().then((decCol) => { + decCollections.push(decCol); + }) + ); + }); + } + return Promise.all(collectionPromises); + }) + ); + + promises.push( + this.apiService.getCiphersOrganization(organizationId).then((ciphers) => { + const cipherPromises: any = []; + if (ciphers != null && ciphers.data != null && ciphers.data.length > 0) { + ciphers.data + .filter((c) => c.deletedDate === null) + .forEach((c) => { + const cipher = new Cipher(new CipherData(c)); + cipherPromises.push( + cipher.decrypt().then((decCipher) => { + decCiphers.push(decCipher); + }) + ); + }); + } + return Promise.all(cipherPromises); + }) + ); + + await Promise.all(promises); + + if (format === "csv") { + const collectionsMap = new Map(); + decCollections.forEach((c) => { + collectionsMap.set(c.id, c); + }); + + const exportCiphers: any[] = []; + decCiphers.forEach((c) => { + // only export logins and secure notes + if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { + return; + } + + const cipher: any = {}; + cipher.collections = []; + if (c.collectionIds != null) { + cipher.collections = c.collectionIds + .filter((id) => collectionsMap.has(id)) + .map((id) => collectionsMap.get(id).name); + } + this.buildCommonCipher(cipher, c); + exportCiphers.push(cipher); + }); + + return papa.unparse(exportCiphers); + } else { + const jsonDoc: any = { + encrypted: false, + collections: [], + items: [], + }; + + decCollections.forEach((c) => { + const collection = new CollectionExport(); + collection.build(c); + jsonDoc.collections.push(collection); + }); + + decCiphers.forEach((c) => { + const cipher = new CipherExport(); + cipher.build(c); + jsonDoc.items.push(cipher); + }); + return JSON.stringify(jsonDoc, null, " "); + } + } + + private async getOrganizationEncryptedExport(organizationId: string): Promise { + const collections: Collection[] = []; + const ciphers: Cipher[] = []; + const promises = []; + + promises.push( + this.apiService.getCollections(organizationId).then((c) => { + const collectionPromises: any = []; + if (c != null && c.data != null && c.data.length > 0) { + c.data.forEach((r) => { + const collection = new Collection(new CollectionData(r as CollectionDetailsResponse)); + collections.push(collection); + }); + } + return Promise.all(collectionPromises); + }) + ); + + promises.push( + this.apiService.getCiphersOrganization(organizationId).then((c) => { + const cipherPromises: any = []; + if (c != null && c.data != null && c.data.length > 0) { + c.data + .filter((item) => item.deletedDate === null) + .forEach((item) => { + const cipher = new Cipher(new CipherData(item)); + ciphers.push(cipher); + }); + } + return Promise.all(cipherPromises); + }) + ); + + await Promise.all(promises); + + const orgKey = await this.cryptoService.getOrgKey(organizationId); + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), orgKey); + + const jsonDoc: any = { + encrypted: true, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, + collections: [], + items: [], + }; + + collections.forEach((c) => { + const collection = new CollectionExport(); + collection.build(c); + jsonDoc.collections.push(collection); + }); + + ciphers.forEach((c) => { + const cipher = new CipherExport(); + cipher.build(c); + jsonDoc.items.push(cipher); + }); + return JSON.stringify(jsonDoc, null, " "); + } + + private padNumber(num: number, width: number, padCharacter = "0"): string { + const numString = num.toString(); + return numString.length >= width + ? numString + : new Array(width - numString.length + 1).join(padCharacter) + numString; + } + + private buildCommonCipher(cipher: any, c: CipherView) { + cipher.type = null; + cipher.name = c.name; + cipher.notes = c.notes; + cipher.fields = null; + cipher.reprompt = c.reprompt; + // Login props + cipher.login_uri = null; + cipher.login_username = null; + cipher.login_password = null; + cipher.login_totp = null; + + if (c.fields) { + c.fields.forEach((f: any) => { + if (!cipher.fields) { + cipher.fields = ""; + } else { + cipher.fields += "\n"; + } + + cipher.fields += (f.name || "") + ": " + f.value; + }); + } + + switch (c.type) { + case CipherType.Login: + cipher.type = "login"; + cipher.login_username = c.login.username; + cipher.login_password = c.login.password; + cipher.login_totp = c.login.totp; + + if (c.login.uris) { + cipher.login_uri = []; + c.login.uris.forEach((u) => { + cipher.login_uri.push(u.uri); + }); + } + break; + case CipherType.SecureNote: + cipher.type = "note"; + break; + default: + return; + } + + return cipher; + } +} diff --git a/jslib/common/src/services/fileUpload.service.ts b/jslib/common/src/services/fileUpload.service.ts new file mode 100644 index 00000000..6fc0af35 --- /dev/null +++ b/jslib/common/src/services/fileUpload.service.ts @@ -0,0 +1,108 @@ +import { ApiService } from "../abstractions/api.service"; +import { FileUploadService as FileUploadServiceAbstraction } from "../abstractions/fileUpload.service"; +import { LogService } from "../abstractions/log.service"; +import { FileUploadType } from "../enums/fileUploadType"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { SendFileUploadDataResponse } from "../models/response/sendFileUploadDataResponse"; + +import { AzureFileUploadService } from "./azureFileUpload.service"; +import { BitwardenFileUploadService } from "./bitwardenFileUpload.service"; + +export class FileUploadService implements FileUploadServiceAbstraction { + private azureFileUploadService: AzureFileUploadService; + private bitwardenFileUploadService: BitwardenFileUploadService; + + constructor(private logService: LogService, private apiService: ApiService) { + this.azureFileUploadService = new AzureFileUploadService(logService); + this.bitwardenFileUploadService = new BitwardenFileUploadService(apiService); + } + + async uploadSendFile( + uploadData: SendFileUploadDataResponse, + fileName: EncString, + encryptedFileData: EncArrayBuffer + ) { + try { + switch (uploadData.fileUploadType) { + case FileUploadType.Direct: + await this.bitwardenFileUploadService.upload( + fileName.encryptedString, + encryptedFileData, + (fd) => + this.apiService.postSendFile( + uploadData.sendResponse.id, + uploadData.sendResponse.file.id, + fd + ) + ); + break; + case FileUploadType.Azure: { + const renewalCallback = async () => { + const renewalResponse = await this.apiService.renewSendFileUploadUrl( + uploadData.sendResponse.id, + uploadData.sendResponse.file.id + ); + return renewalResponse.url; + }; + await this.azureFileUploadService.upload( + uploadData.url, + encryptedFileData, + renewalCallback + ); + break; + } + default: + throw new Error("Unknown file upload type"); + } + } catch (e) { + await this.apiService.deleteSend(uploadData.sendResponse.id); + throw e; + } + } + + async uploadCipherAttachment( + admin: boolean, + uploadData: AttachmentUploadDataResponse, + encryptedFileName: EncString, + encryptedFileData: EncArrayBuffer + ) { + const response = admin ? uploadData.cipherMiniResponse : uploadData.cipherResponse; + try { + switch (uploadData.fileUploadType) { + case FileUploadType.Direct: + await this.bitwardenFileUploadService.upload( + encryptedFileName.encryptedString, + encryptedFileData, + (fd) => this.apiService.postAttachmentFile(response.id, uploadData.attachmentId, fd) + ); + break; + case FileUploadType.Azure: { + const renewalCallback = async () => { + const renewalResponse = await this.apiService.renewAttachmentUploadUrl( + response.id, + uploadData.attachmentId + ); + return renewalResponse.url; + }; + await this.azureFileUploadService.upload( + uploadData.url, + encryptedFileData, + renewalCallback + ); + break; + } + default: + throw new Error("Unknown file upload type."); + } + } catch (e) { + if (admin) { + await this.apiService.deleteCipherAttachmentAdmin(response.id, uploadData.attachmentId); + } else { + await this.apiService.deleteCipherAttachment(response.id, uploadData.attachmentId); + } + throw e; + } + } +} diff --git a/jslib/common/src/services/folder.service.ts b/jslib/common/src/services/folder.service.ts new file mode 100644 index 00000000..666ca6d2 --- /dev/null +++ b/jslib/common/src/services/folder.service.ts @@ -0,0 +1,194 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService as FolderServiceAbstraction } from "../abstractions/folder.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { StateService } from "../abstractions/state.service"; +import { ServiceUtils } from "../misc/serviceUtils"; +import { Utils } from "../misc/utils"; +import { CipherData } from "../models/data/cipherData"; +import { FolderData } from "../models/data/folderData"; +import { Folder } from "../models/domain/folder"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { TreeNode } from "../models/domain/treeNode"; +import { FolderRequest } from "../models/request/folderRequest"; +import { FolderResponse } from "../models/response/folderResponse"; +import { FolderView } from "../models/view/folderView"; + +const NestingDelimiter = "/"; + +export class FolderService implements FolderServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private apiService: ApiService, + private i18nService: I18nService, + private cipherService: CipherService, + private stateService: StateService + ) {} + + async clearCache(userId?: string): Promise { + await this.stateService.setDecryptedFolders(null, { userId: userId }); + } + + async encrypt(model: FolderView, key?: SymmetricCryptoKey): Promise { + const folder = new Folder(); + folder.id = model.id; + folder.name = await this.cryptoService.encrypt(model.name, key); + return folder; + } + + async get(id: string): Promise { + const folders = await this.stateService.getEncryptedFolders(); + // eslint-disable-next-line + if (folders == null || !folders.hasOwnProperty(id)) { + return null; + } + + return new Folder(folders[id]); + } + + async getAll(): Promise { + const folders = await this.stateService.getEncryptedFolders(); + const response: Folder[] = []; + for (const id in folders) { + // eslint-disable-next-line + if (folders.hasOwnProperty(id)) { + response.push(new Folder(folders[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + const decryptedFolders = await this.stateService.getDecryptedFolders(); + if (decryptedFolders != null) { + return decryptedFolders; + } + + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const decFolders: FolderView[] = []; + const promises: Promise[] = []; + const folders = await this.getAll(); + folders.forEach((folder) => { + promises.push(folder.decrypt().then((f) => decFolders.push(f))); + }); + + await Promise.all(promises); + decFolders.sort(Utils.getSortFunction(this.i18nService, "name")); + + const noneFolder = new FolderView(); + noneFolder.name = this.i18nService.t("noneFolder"); + decFolders.push(noneFolder); + + await this.stateService.setDecryptedFolders(decFolders); + return decFolders; + } + + async getAllNested(): Promise[]> { + const folders = await this.getAllDecrypted(); + const nodes: TreeNode[] = []; + folders.forEach((f) => { + const folderCopy = new FolderView(); + folderCopy.id = f.id; + folderCopy.revisionDate = f.revisionDate; + const parts = f.name != null ? f.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, folderCopy, null, NestingDelimiter); + }); + return nodes; + } + + async getNested(id: string): Promise> { + const folders = await this.getAllNested(); + return ServiceUtils.getTreeNodeObject(folders, id) as TreeNode; + } + + async saveWithServer(folder: Folder): Promise { + const request = new FolderRequest(folder); + + let response: FolderResponse; + if (folder.id == null) { + response = await this.apiService.postFolder(request); + folder.id = response.id; + } else { + response = await this.apiService.putFolder(folder.id, request); + } + + const userId = await this.stateService.getUserId(); + const data = new FolderData(response, userId); + await this.upsert(data); + } + + async upsert(folder: FolderData | FolderData[]): Promise { + let folders = await this.stateService.getEncryptedFolders(); + if (folders == null) { + folders = {}; + } + + if (folder instanceof FolderData) { + const f = folder as FolderData; + folders[f.id] = f; + } else { + (folder as FolderData[]).forEach((f) => { + folders[f.id] = f; + }); + } + + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + } + + async replace(folders: { [id: string]: FolderData }): Promise { + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + } + + async clear(userId?: string): Promise { + await this.stateService.setDecryptedFolders(null, { userId: userId }); + await this.stateService.setEncryptedFolders(null, { userId: userId }); + } + + async delete(id: string | string[]): Promise { + const folders = await this.stateService.getEncryptedFolders(); + if (folders == null) { + return; + } + + if (typeof id === "string") { + if (folders[id] == null) { + return; + } + delete folders[id]; + } else { + (id as string[]).forEach((i) => { + delete folders[i]; + }); + } + + await this.stateService.setDecryptedFolders(null); + await this.stateService.setEncryptedFolders(folders); + + // Items in a deleted folder are re-assigned to "No Folder" + const ciphers = await this.stateService.getEncryptedCiphers(); + if (ciphers != null) { + const updates: CipherData[] = []; + for (const cId in ciphers) { + if (ciphers[cId].folderId === id) { + ciphers[cId].folderId = null; + updates.push(ciphers[cId]); + } + } + if (updates.length > 0) { + this.cipherService.upsert(updates); + } + } + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteFolder(id); + await this.delete(id); + } +} diff --git a/jslib/common/src/services/i18n.service.ts b/jslib/common/src/services/i18n.service.ts new file mode 100644 index 00000000..fda8f839 --- /dev/null +++ b/jslib/common/src/services/i18n.service.ts @@ -0,0 +1,175 @@ +import { I18nService as I18nServiceAbstraction } from "../abstractions/i18n.service"; + +export class I18nService implements I18nServiceAbstraction { + locale: string; + // First locale is the default (English) + supportedTranslationLocales: string[] = ["en"]; + translationLocale: string; + collator: Intl.Collator; + localeNames = new Map([ + ["af", "Afrikaans"], + ["az", "Azərbaycanca"], + ["be", "Беларуская"], + ["bg", "български"], + ["bn", "বাংলা"], + ["bs", "bosanski jezik"], + ["ca", "català"], + ["cs", "čeština"], + ["da", "dansk"], + ["de", "Deutsch"], + ["el", "Ελληνικά"], + ["en", "English"], + ["en-GB", "English (British)"], + ["en-IN", "English (India)"], + ["eo", "Esperanto"], + ["es", "español"], + ["et", "eesti"], + ["fa", "فارسی"], + ["fi", "suomi"], + ["fil", "Wikang Filipino"], + ["fr", "français"], + ["he", "עברית"], + ["hi", "हिन्दी"], + ["hr", "hrvatski"], + ["hu", "magyar"], + ["id", "Bahasa Indonesia"], + ["it", "italiano"], + ["ja", "日本語"], + ["ka", "ქართული"], + ["km", "ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ"], + ["kn", "ಕನ್ನಡ"], + ["ko", "한국어"], + ["lt", "lietuvių kalba"], + ["lv", "Latvietis"], + ["me", "црногорски"], + ["ml", "മലയാളം"], + ["nb", "norsk (bokmål)"], + ["nl", "Nederlands"], + ["nn", "Norsk Nynorsk"], + ["pl", "polski"], + ["pt-BR", "português do Brasil"], + ["pt-PT", "português"], + ["ro", "română"], + ["ru", "русский"], + ["si", "සිංහල"], + ["sk", "slovenčina"], + ["sl", "Slovenski jezik, Slovenščina"], + ["sr", "Српски"], + ["sv", "svenska"], + ["th", "ไทย"], + ["tr", "Türkçe"], + ["uk", "українська"], + ["vi", "Tiếng Việt"], + ["zh-CN", "中文(中国大陆)"], + ["zh-TW", "中文(台灣)"], + ]); + + protected inited: boolean; + protected defaultMessages: any = {}; + protected localeMessages: any = {}; + + constructor( + protected systemLanguage: string, + protected localesDirectory: string, + protected getLocalesJson: (formattedLocale: string) => Promise + ) { + this.systemLanguage = systemLanguage.replace("_", "-"); + } + + async init(locale?: string) { + if (this.inited) { + throw new Error("i18n already initialized."); + } + if (this.supportedTranslationLocales == null || this.supportedTranslationLocales.length === 0) { + throw new Error("supportedTranslationLocales not set."); + } + + this.inited = true; + this.locale = this.translationLocale = locale != null ? locale : this.systemLanguage; + + try { + this.collator = new Intl.Collator(this.locale, { numeric: true, sensitivity: "base" }); + } catch { + this.collator = null; + } + + if (this.supportedTranslationLocales.indexOf(this.translationLocale) === -1) { + this.translationLocale = this.translationLocale.slice(0, 2); + + if (this.supportedTranslationLocales.indexOf(this.translationLocale) === -1) { + this.translationLocale = this.supportedTranslationLocales[0]; + } + } + + if (this.localesDirectory != null) { + await this.loadMessages(this.translationLocale, this.localeMessages); + if (this.translationLocale !== this.supportedTranslationLocales[0]) { + await this.loadMessages(this.supportedTranslationLocales[0], this.defaultMessages); + } + } + } + + t(id: string, p1?: string, p2?: string, p3?: string): string { + return this.translate(id, p1, p2, p3); + } + + translate(id: string, p1?: string, p2?: string, p3?: string): string { + let result: string; + // eslint-disable-next-line + if (this.localeMessages.hasOwnProperty(id) && this.localeMessages[id]) { + result = this.localeMessages[id]; + // eslint-disable-next-line + } else if (this.defaultMessages.hasOwnProperty(id) && this.defaultMessages[id]) { + result = this.defaultMessages[id]; + } else { + result = ""; + } + + if (result !== "") { + if (p1 != null) { + result = result.split("__$1__").join(p1); + } + if (p2 != null) { + result = result.split("__$2__").join(p2); + } + if (p3 != null) { + result = result.split("__$3__").join(p3); + } + } + + return result; + } + + private async loadMessages(locale: string, messagesObj: any): Promise { + const formattedLocale = locale.replace("-", "_"); + const locales = await this.getLocalesJson(formattedLocale); + for (const prop in locales) { + // eslint-disable-next-line + if (!locales.hasOwnProperty(prop)) { + continue; + } + messagesObj[prop] = locales[prop].message; + + if (locales[prop].placeholders) { + for (const placeProp in locales[prop].placeholders) { + if ( + !locales[prop].placeholders.hasOwnProperty(placeProp) || // eslint-disable-line + !locales[prop].placeholders[placeProp].content + ) { + continue; + } + + const replaceToken = "\\$" + placeProp.toUpperCase() + "\\$"; + let replaceContent = locales[prop].placeholders[placeProp].content; + if (replaceContent === "$1" || replaceContent === "$2" || replaceContent === "$3") { + replaceContent = "__$" + replaceContent + "__"; + } + messagesObj[prop] = messagesObj[prop].replace( + new RegExp(replaceToken, "g"), + replaceContent + ); + } + } + } + } +} diff --git a/jslib/common/src/services/import.service.ts b/jslib/common/src/services/import.service.ts new file mode 100644 index 00000000..86226d7d --- /dev/null +++ b/jslib/common/src/services/import.service.ts @@ -0,0 +1,382 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { ImportService as ImportServiceAbstraction } from "../abstractions/import.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { CipherType } from "../enums/cipherType"; +import { + featuredImportOptions, + ImportOption, + ImportType, + regularImportOptions, +} from "../enums/importOptions"; +import { AscendoCsvImporter } from "../importers/ascendoCsvImporter"; +import { AvastCsvImporter } from "../importers/avastCsvImporter"; +import { AvastJsonImporter } from "../importers/avastJsonImporter"; +import { AviraCsvImporter } from "../importers/aviraCsvImporter"; +import { BitwardenCsvImporter } from "../importers/bitwardenCsvImporter"; +import { BitwardenJsonImporter } from "../importers/bitwardenJsonImporter"; +import { BitwardenPasswordProtectedImporter } from "../importers/bitwardenPasswordProtectedImporter"; +import { BlackBerryCsvImporter } from "../importers/blackBerryCsvImporter"; +import { BlurCsvImporter } from "../importers/blurCsvImporter"; +import { ButtercupCsvImporter } from "../importers/buttercupCsvImporter"; +import { ChromeCsvImporter } from "../importers/chromeCsvImporter"; +import { ClipperzHtmlImporter } from "../importers/clipperzHtmlImporter"; +import { CodebookCsvImporter } from "../importers/codebookCsvImporter"; +import { DashlaneCsvImporter } from "../importers/dashlaneImporters/dashlaneCsvImporter"; +import { DashlaneJsonImporter } from "../importers/dashlaneImporters/dashlaneJsonImporter"; +import { EncryptrCsvImporter } from "../importers/encryptrCsvImporter"; +import { EnpassCsvImporter } from "../importers/enpassCsvImporter"; +import { EnpassJsonImporter } from "../importers/enpassJsonImporter"; +import { FirefoxCsvImporter } from "../importers/firefoxCsvImporter"; +import { FSecureFskImporter } from "../importers/fsecureFskImporter"; +import { GnomeJsonImporter } from "../importers/gnomeJsonImporter"; +import { ImportError } from "../importers/importError"; +import { Importer } from "../importers/importer"; +import { KasperskyTxtImporter } from "../importers/kasperskyTxtImporter"; +import { KeePass2XmlImporter } from "../importers/keepass2XmlImporter"; +import { KeePassXCsvImporter } from "../importers/keepassxCsvImporter"; +import { KeeperCsvImporter } from "../importers/keeperImporters/keeperCsvImporter"; +import { LastPassCsvImporter } from "../importers/lastpassCsvImporter"; +import { LogMeOnceCsvImporter } from "../importers/logMeOnceCsvImporter"; +import { MeldiumCsvImporter } from "../importers/meldiumCsvImporter"; +import { MSecureCsvImporter } from "../importers/msecureCsvImporter"; +import { MykiCsvImporter } from "../importers/mykiCsvImporter"; +import { NordPassCsvImporter } from "../importers/nordpassCsvImporter"; +import { OnePassword1PifImporter } from "../importers/onepasswordImporters/onepassword1PifImporter"; +import { OnePassword1PuxImporter } from "../importers/onepasswordImporters/onepassword1PuxImporter"; +import { OnePasswordMacCsvImporter } from "../importers/onepasswordImporters/onepasswordMacCsvImporter"; +import { OnePasswordWinCsvImporter } from "../importers/onepasswordImporters/onepasswordWinCsvImporter"; +import { PadlockCsvImporter } from "../importers/padlockCsvImporter"; +import { PassKeepCsvImporter } from "../importers/passkeepCsvImporter"; +import { PassmanJsonImporter } from "../importers/passmanJsonImporter"; +import { PasspackCsvImporter } from "../importers/passpackCsvImporter"; +import { PasswordAgentCsvImporter } from "../importers/passwordAgentCsvImporter"; +import { PasswordBossJsonImporter } from "../importers/passwordBossJsonImporter"; +import { PasswordDragonXmlImporter } from "../importers/passwordDragonXmlImporter"; +import { PasswordSafeXmlImporter } from "../importers/passwordSafeXmlImporter"; +import { PasswordWalletTxtImporter } from "../importers/passwordWalletTxtImporter"; +import { RememBearCsvImporter } from "../importers/rememBearCsvImporter"; +import { RoboFormCsvImporter } from "../importers/roboformCsvImporter"; +import { SafariCsvImporter } from "../importers/safariCsvImporter"; +import { SafeInCloudXmlImporter } from "../importers/safeInCloudXmlImporter"; +import { SaferPassCsvImporter } from "../importers/saferpassCsvImport"; +import { SecureSafeCsvImporter } from "../importers/secureSafeCsvImporter"; +import { SplashIdCsvImporter } from "../importers/splashIdCsvImporter"; +import { StickyPasswordXmlImporter } from "../importers/stickyPasswordXmlImporter"; +import { TrueKeyCsvImporter } from "../importers/truekeyCsvImporter"; +import { UpmCsvImporter } from "../importers/upmCsvImporter"; +import { YotiCsvImporter } from "../importers/yotiCsvImporter"; +import { ZohoVaultCsvImporter } from "../importers/zohoVaultCsvImporter"; +import { Utils } from "../misc/utils"; +import { ImportResult } from "../models/domain/importResult"; +import { CipherRequest } from "../models/request/cipherRequest"; +import { CollectionRequest } from "../models/request/collectionRequest"; +import { FolderRequest } from "../models/request/folderRequest"; +import { ImportCiphersRequest } from "../models/request/importCiphersRequest"; +import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest"; +import { KvpRequest } from "../models/request/kvpRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { CipherView } from "../models/view/cipherView"; + +export class ImportService implements ImportServiceAbstraction { + featuredImportOptions = featuredImportOptions as readonly ImportOption[]; + + regularImportOptions = regularImportOptions as readonly ImportOption[]; + + constructor( + private cipherService: CipherService, + private folderService: FolderService, + private apiService: ApiService, + private i18nService: I18nService, + private collectionService: CollectionService, + private platformUtilsService: PlatformUtilsService, + private cryptoService: CryptoService + ) {} + + getImportOptions(): ImportOption[] { + return this.featuredImportOptions.concat(this.regularImportOptions); + } + + async import( + importer: Importer, + fileContents: string, + organizationId: string = null + ): Promise { + const importResult = await importer.parse(fileContents); + if (importResult.success) { + if (importResult.folders.length === 0 && importResult.ciphers.length === 0) { + return new ImportError(this.i18nService.t("importNothingError")); + } else if (importResult.ciphers.length > 0) { + const halfway = Math.floor(importResult.ciphers.length / 2); + const last = importResult.ciphers.length - 1; + + if ( + this.badData(importResult.ciphers[0]) && + this.badData(importResult.ciphers[halfway]) && + this.badData(importResult.ciphers[last]) + ) { + return new ImportError(this.i18nService.t("importFormatError")); + } + } + try { + await this.postImport(importResult, organizationId); + } catch (error) { + const errorResponse = new ErrorResponse(error, 400); + return this.handleServerError(errorResponse, importResult); + } + return null; + } else { + if (!Utils.isNullOrWhitespace(importResult.errorMessage)) { + return new ImportError(importResult.errorMessage, importResult.missingPassword); + } else { + return new ImportError( + this.i18nService.t("importFormatError"), + importResult.missingPassword + ); + } + } + } + + getImporter( + format: ImportType | "bitwardenpasswordprotected", + organizationId: string = null, + password: string = null + ): Importer { + const importer = this.getImporterInstance(format, password); + if (importer == null) { + return null; + } + importer.organizationId = organizationId; + return importer; + } + + private getImporterInstance(format: ImportType | "bitwardenpasswordprotected", password: string) { + if (format == null) { + return null; + } + + switch (format) { + case "bitwardencsv": + return new BitwardenCsvImporter(); + case "bitwardenjson": + return new BitwardenJsonImporter(this.cryptoService, this.i18nService); + case "bitwardenpasswordprotected": + return new BitwardenPasswordProtectedImporter( + this.cryptoService, + this.i18nService, + password + ); + case "lastpasscsv": + case "passboltcsv": + return new LastPassCsvImporter(); + case "keepassxcsv": + return new KeePassXCsvImporter(); + case "aviracsv": + return new AviraCsvImporter(); + case "blurcsv": + return new BlurCsvImporter(); + case "safeincloudxml": + return new SafeInCloudXmlImporter(); + case "padlockcsv": + return new PadlockCsvImporter(); + case "keepass2xml": + return new KeePass2XmlImporter(); + case "chromecsv": + case "operacsv": + case "vivaldicsv": + return new ChromeCsvImporter(); + case "firefoxcsv": + return new FirefoxCsvImporter(); + case "upmcsv": + return new UpmCsvImporter(); + case "saferpasscsv": + return new SaferPassCsvImporter(); + case "safaricsv": + return new SafariCsvImporter(); + case "meldiumcsv": + return new MeldiumCsvImporter(); + case "1password1pif": + return new OnePassword1PifImporter(); + case "1password1pux": + return new OnePassword1PuxImporter(); + case "1passwordwincsv": + return new OnePasswordWinCsvImporter(); + case "1passwordmaccsv": + return new OnePasswordMacCsvImporter(); + case "keepercsv": + return new KeeperCsvImporter(); + // case "keeperjson": + // return new KeeperJsonImporter(); + case "passworddragonxml": + return new PasswordDragonXmlImporter(); + case "enpasscsv": + return new EnpassCsvImporter(); + case "enpassjson": + return new EnpassJsonImporter(); + case "pwsafexml": + return new PasswordSafeXmlImporter(); + case "dashlanecsv": + return new DashlaneCsvImporter(); + case "dashlanejson": + return new DashlaneJsonImporter(); + case "msecurecsv": + return new MSecureCsvImporter(); + case "stickypasswordxml": + return new StickyPasswordXmlImporter(); + case "truekeycsv": + return new TrueKeyCsvImporter(); + case "clipperzhtml": + return new ClipperzHtmlImporter(); + case "roboformcsv": + return new RoboFormCsvImporter(); + case "ascendocsv": + return new AscendoCsvImporter(); + case "passwordbossjson": + return new PasswordBossJsonImporter(); + case "zohovaultcsv": + return new ZohoVaultCsvImporter(); + case "splashidcsv": + return new SplashIdCsvImporter(); + case "passkeepcsv": + return new PassKeepCsvImporter(); + case "gnomejson": + return new GnomeJsonImporter(); + case "passwordagentcsv": + return new PasswordAgentCsvImporter(); + case "passpackcsv": + return new PasspackCsvImporter(); + case "passmanjson": + return new PassmanJsonImporter(); + case "avastcsv": + return new AvastCsvImporter(); + case "avastjson": + return new AvastJsonImporter(); + case "fsecurefsk": + return new FSecureFskImporter(); + case "kasperskytxt": + return new KasperskyTxtImporter(); + case "remembearcsv": + return new RememBearCsvImporter(); + case "passwordwallettxt": + return new PasswordWalletTxtImporter(); + case "mykicsv": + return new MykiCsvImporter(); + case "securesafecsv": + return new SecureSafeCsvImporter(); + case "logmeoncecsv": + return new LogMeOnceCsvImporter(); + case "blackberrycsv": + return new BlackBerryCsvImporter(); + case "buttercupcsv": + return new ButtercupCsvImporter(); + case "codebookcsv": + return new CodebookCsvImporter(); + case "encryptrcsv": + return new EncryptrCsvImporter(); + case "yoticsv": + return new YotiCsvImporter(); + case "nordpasscsv": + return new NordPassCsvImporter(); + default: + return null; + } + } + + private async postImport(importResult: ImportResult, organizationId: string = null) { + if (organizationId == null) { + const request = new ImportCiphersRequest(); + for (let i = 0; i < importResult.ciphers.length; i++) { + const c = await this.cipherService.encrypt(importResult.ciphers[i]); + request.ciphers.push(new CipherRequest(c)); + } + if (importResult.folders != null) { + for (let i = 0; i < importResult.folders.length; i++) { + const f = await this.folderService.encrypt(importResult.folders[i]); + request.folders.push(new FolderRequest(f)); + } + } + if (importResult.folderRelationships != null) { + importResult.folderRelationships.forEach((r) => + request.folderRelationships.push(new KvpRequest(r[0], r[1])) + ); + } + return await this.apiService.postImportCiphers(request); + } else { + const request = new ImportOrganizationCiphersRequest(); + for (let i = 0; i < importResult.ciphers.length; i++) { + importResult.ciphers[i].organizationId = organizationId; + const c = await this.cipherService.encrypt(importResult.ciphers[i]); + request.ciphers.push(new CipherRequest(c)); + } + if (importResult.collections != null) { + for (let i = 0; i < importResult.collections.length; i++) { + importResult.collections[i].organizationId = organizationId; + const c = await this.collectionService.encrypt(importResult.collections[i]); + request.collections.push(new CollectionRequest(c)); + } + } + if (importResult.collectionRelationships != null) { + importResult.collectionRelationships.forEach((r) => + request.collectionRelationships.push(new KvpRequest(r[0], r[1])) + ); + } + return await this.apiService.postImportOrganizationCiphers(organizationId, request); + } + } + + private badData(c: CipherView) { + return ( + (c.name == null || c.name === "--") && + c.type === CipherType.Login && + c.login != null && + Utils.isNullOrWhitespace(c.login.password) + ); + } + + private handleServerError(errorResponse: ErrorResponse, importResult: ImportResult): ImportError { + if (errorResponse.validationErrors == null) { + return new ImportError(errorResponse.message); + } + + let errorMessage = ""; + + Object.entries(errorResponse.validationErrors).forEach(([key, value], index) => { + let item; + let itemType; + const i = Number(key.match(/[0-9]+/)[0]); + + switch (key.match(/^\w+/)[0]) { + case "Ciphers": + item = importResult.ciphers[i]; + itemType = CipherType[item.type]; + break; + case "Folders": + item = importResult.folders[i]; + itemType = "Folder"; + break; + case "Collections": + item = importResult.collections[i]; + itemType = "Collection"; + break; + default: + return; + } + + if (index > 0) { + errorMessage += "\n\n"; + } + + if (itemType !== "Folder" && itemType !== "Collection") { + errorMessage += "[" + (i + 1) + "] "; + } + + errorMessage += "[" + itemType + '] "' + item.name + '": ' + value; + }); + + return new ImportError(errorMessage); + } +} diff --git a/jslib/common/src/services/keyConnector.service.ts b/jslib/common/src/services/keyConnector.service.ts new file mode 100644 index 00000000..c7e8b991 --- /dev/null +++ b/jslib/common/src/services/keyConnector.service.ts @@ -0,0 +1,134 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { OrganizationUserType } from "../enums/organizationUserType"; +import { Utils } from "../misc/utils"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest"; +import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest"; +import { KeysRequest } from "../models/request/keysRequest"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export class KeyConnectorService implements KeyConnectorServiceAbstraction { + constructor( + private stateService: StateService, + private cryptoService: CryptoService, + private apiService: ApiService, + private tokenService: TokenService, + private logService: LogService, + private organizationService: OrganizationService, + private cryptoFunctionService: CryptoFunctionService + ) {} + + setUsesKeyConnector(usesKeyConnector: boolean) { + return this.stateService.setUsesKeyConnector(usesKeyConnector); + } + + async getUsesKeyConnector(): Promise { + return await this.stateService.getUsesKeyConnector(); + } + + async userNeedsMigration() { + const loggedInUsingSso = await this.tokenService.getIsExternal(); + const requiredByOrganization = (await this.getManagingOrganization()) != null; + const userIsNotUsingKeyConnector = !(await this.getUsesKeyConnector()); + + return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector; + } + + async migrateUser() { + const organization = await this.getManagingOrganization(); + const key = await this.cryptoService.getKey(); + const keyConnectorRequest = new KeyConnectorUserKeyRequest(key.encKeyB64); + + try { + await this.apiService.postUserKeyToKeyConnector( + organization.keyConnectorUrl, + keyConnectorRequest + ); + } catch (e) { + throw new Error("Unable to reach key connector"); + } + + await this.apiService.postConvertToKeyConnector(); + } + + async getAndSetKey(url: string) { + try { + const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url); + const keyArr = Utils.fromB64ToArray(userKeyResponse.key); + const k = new SymmetricCryptoKey(keyArr); + await this.cryptoService.setKey(k); + } catch (e) { + this.logService.error(e); + throw new Error("Unable to reach key connector"); + } + } + + async getManagingOrganization() { + const orgs = await this.organizationService.getAll(); + return orgs.find( + (o) => + o.keyConnectorEnabled && + o.type !== OrganizationUserType.Admin && + o.type !== OrganizationUserType.Owner && + !o.isProviderUser + ); + } + + async convertNewSsoUserToKeyConnector(tokenResponse: IdentityTokenResponse, orgId: string) { + const { kdf, kdfIterations, keyConnectorUrl } = tokenResponse; + const password = await this.cryptoFunctionService.randomBytes(64); + + const k = await this.cryptoService.makeKey( + Utils.fromBufferToB64(password), + await this.tokenService.getEmail(), + kdf, + kdfIterations + ); + const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64); + await this.cryptoService.setKey(k); + + const encKey = await this.cryptoService.makeEncKey(k); + await this.cryptoService.setEncKey(encKey[1].encryptedString); + + const [pubKey, privKey] = await this.cryptoService.makeKeyPair(); + + try { + await this.apiService.postUserKeyToKeyConnector(keyConnectorUrl, keyConnectorRequest); + } catch (e) { + throw new Error("Unable to reach key connector"); + } + + const keys = new KeysRequest(pubKey, privKey.encryptedString); + const setPasswordRequest = new SetKeyConnectorKeyRequest( + encKey[1].encryptedString, + kdf, + kdfIterations, + orgId, + keys + ); + await this.apiService.postSetKeyConnectorKey(setPasswordRequest); + } + + async setConvertAccountRequired(status: boolean) { + await this.stateService.setConvertAccountToKeyConnector(status); + } + + async getConvertAccountRequired(): Promise { + return await this.stateService.getConvertAccountToKeyConnector(); + } + + async removeConvertAccountRequired() { + await this.stateService.setConvertAccountToKeyConnector(null); + } + + async clear() { + await this.removeConvertAccountRequired(); + } +} diff --git a/jslib/common/src/services/noopMessaging.service.ts b/jslib/common/src/services/noopMessaging.service.ts new file mode 100644 index 00000000..d1a60bc5 --- /dev/null +++ b/jslib/common/src/services/noopMessaging.service.ts @@ -0,0 +1,7 @@ +import { MessagingService } from "../abstractions/messaging.service"; + +export class NoopMessagingService implements MessagingService { + send(subscriber: string, arg: any = {}) { + // Do nothing... + } +} diff --git a/jslib/common/src/services/notifications.service.ts b/jslib/common/src/services/notifications.service.ts new file mode 100644 index 00000000..d4e3fef4 --- /dev/null +++ b/jslib/common/src/services/notifications.service.ts @@ -0,0 +1,231 @@ +import * as signalR from "@microsoft/signalr"; +import * as signalRMsgPack from "@microsoft/signalr-protocol-msgpack"; + +import { ApiService } from "../abstractions/api.service"; +import { AppIdService } from "../abstractions/appId.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { LogService } from "../abstractions/log.service"; +import { NotificationsService as NotificationsServiceAbstraction } from "../abstractions/notifications.service"; +import { StateService } from "../abstractions/state.service"; +import { SyncService } from "../abstractions/sync.service"; +import { VaultTimeoutService } from "../abstractions/vaultTimeout.service"; +import { NotificationType } from "../enums/notificationType"; +import { + NotificationResponse, + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; + +export class NotificationsService implements NotificationsServiceAbstraction { + private signalrConnection: signalR.HubConnection; + private url: string; + private connected = false; + private inited = false; + private inactive = false; + private reconnectTimer: any = null; + + constructor( + private syncService: SyncService, + private appIdService: AppIdService, + private apiService: ApiService, + private vaultTimeoutService: VaultTimeoutService, + private environmentService: EnvironmentService, + private logoutCallback: () => Promise, + private logService: LogService, + private stateService: StateService + ) { + this.environmentService.urls.subscribe(() => { + if (!this.inited) { + return; + } + + this.init(); + }); + } + + async init(): Promise { + this.inited = false; + this.url = this.environmentService.getNotificationsUrl(); + + // Set notifications server URL to `https://-` to effectively disable communication + // with the notifications server from the client app + if (this.url === "https://-") { + return; + } + + if (this.signalrConnection != null) { + this.signalrConnection.off("ReceiveMessage"); + this.signalrConnection.off("Heartbeat"); + await this.signalrConnection.stop(); + this.connected = false; + this.signalrConnection = null; + } + + this.signalrConnection = new signalR.HubConnectionBuilder() + .withUrl(this.url + "/hub", { + accessTokenFactory: () => this.apiService.getActiveBearerToken(), + skipNegotiation: true, + transport: signalR.HttpTransportType.WebSockets, + }) + .withHubProtocol(new signalRMsgPack.MessagePackHubProtocol() as signalR.IHubProtocol) + // .configureLogging(signalR.LogLevel.Trace) + .build(); + + this.signalrConnection.on("ReceiveMessage", (data: any) => + this.processNotification(new NotificationResponse(data)) + ); + // eslint-disable-next-line + this.signalrConnection.on("Heartbeat", (data: any) => { + /*console.log('Heartbeat!');*/ + }); + this.signalrConnection.onclose(() => { + this.connected = false; + this.reconnect(true); + }); + this.inited = true; + if (await this.isAuthedAndUnlocked()) { + await this.reconnect(false); + } + } + + async updateConnection(sync = false): Promise { + if (!this.inited) { + return; + } + try { + if (await this.isAuthedAndUnlocked()) { + await this.reconnect(sync); + } else { + await this.signalrConnection.stop(); + } + } catch (e) { + this.logService.error(e.toString()); + } + } + + async reconnectFromActivity(): Promise { + this.inactive = false; + if (this.inited && !this.connected) { + await this.reconnect(true); + } + } + + async disconnectFromInactivity(): Promise { + this.inactive = true; + if (this.inited && this.connected) { + await this.signalrConnection.stop(); + } + } + + private async processNotification(notification: NotificationResponse) { + const appId = await this.appIdService.getAppId(); + if (notification == null || notification.contextId === appId) { + return; + } + + const isAuthenticated = await this.stateService.getIsAuthenticated(); + const payloadUserId = notification.payload.userId || notification.payload.UserId; + const myUserId = await this.stateService.getUserId(); + if (isAuthenticated && payloadUserId != null && payloadUserId !== myUserId) { + return; + } + + switch (notification.type) { + case NotificationType.SyncCipherCreate: + case NotificationType.SyncCipherUpdate: + await this.syncService.syncUpsertCipher( + notification.payload as SyncCipherNotification, + notification.type === NotificationType.SyncCipherUpdate + ); + break; + case NotificationType.SyncCipherDelete: + case NotificationType.SyncLoginDelete: + await this.syncService.syncDeleteCipher(notification.payload as SyncCipherNotification); + break; + case NotificationType.SyncFolderCreate: + case NotificationType.SyncFolderUpdate: + await this.syncService.syncUpsertFolder( + notification.payload as SyncFolderNotification, + notification.type === NotificationType.SyncFolderUpdate + ); + break; + case NotificationType.SyncFolderDelete: + await this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification); + break; + case NotificationType.SyncVault: + case NotificationType.SyncCiphers: + case NotificationType.SyncSettings: + if (isAuthenticated) { + await this.syncService.fullSync(false); + } + break; + case NotificationType.SyncOrgKeys: + if (isAuthenticated) { + await this.syncService.fullSync(true); + // Stop so a reconnect can be made + await this.signalrConnection.stop(); + } + break; + case NotificationType.LogOut: + if (isAuthenticated) { + this.logoutCallback(); + } + break; + case NotificationType.SyncSendCreate: + case NotificationType.SyncSendUpdate: + await this.syncService.syncUpsertSend( + notification.payload as SyncSendNotification, + notification.type === NotificationType.SyncSendUpdate + ); + break; + case NotificationType.SyncSendDelete: + await this.syncService.syncDeleteSend(notification.payload as SyncSendNotification); + break; + default: + break; + } + } + + private async reconnect(sync: boolean) { + if (this.reconnectTimer != null) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + if (this.connected || !this.inited || this.inactive) { + return; + } + const authedAndUnlocked = await this.isAuthedAndUnlocked(); + if (!authedAndUnlocked) { + return; + } + + try { + await this.signalrConnection.start(); + this.connected = true; + if (sync) { + await this.syncService.fullSync(false); + } + } catch (e) { + this.logService.error(e); + } + + if (!this.connected) { + this.reconnectTimer = setTimeout(() => this.reconnect(sync), this.random(120000, 300000)); + } + } + + private async isAuthedAndUnlocked() { + if (await this.stateService.getIsAuthenticated()) { + const locked = await this.vaultTimeoutService.isLocked(); + return !locked; + } + return false; + } + + private random(min: number, max: number) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; + } +} diff --git a/jslib/common/src/services/organization.service.ts b/jslib/common/src/services/organization.service.ts new file mode 100644 index 00000000..0ff32e81 --- /dev/null +++ b/jslib/common/src/services/organization.service.ts @@ -0,0 +1,55 @@ +import { OrganizationService as OrganizationServiceAbstraction } from "../abstractions/organization.service"; +import { StateService } from "../abstractions/state.service"; +import { OrganizationData } from "../models/data/organizationData"; +import { Organization } from "../models/domain/organization"; + +export class OrganizationService implements OrganizationServiceAbstraction { + constructor(private stateService: StateService) {} + + async get(id: string): Promise { + const organizations = await this.stateService.getOrganizations(); + // eslint-disable-next-line + if (organizations == null || !organizations.hasOwnProperty(id)) { + return null; + } + + return new Organization(organizations[id]); + } + + async getByIdentifier(identifier: string): Promise { + const organizations = await this.getAll(); + if (organizations == null || organizations.length === 0) { + return null; + } + + return organizations.find((o) => o.identifier === identifier); + } + + async getAll(userId?: string): Promise { + const organizations = await this.stateService.getOrganizations({ userId: userId }); + const response: Organization[] = []; + for (const id in organizations) { + // eslint-disable-next-line + if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) { + response.push(new Organization(organizations[id])); + } + } + return response; + } + + async save(organizations: { [id: string]: OrganizationData }) { + return await this.stateService.setOrganizations(organizations); + } + + async canManageSponsorships(): Promise { + const orgs = await this.getAll(); + return orgs.some( + (o) => o.familySponsorshipAvailable || o.familySponsorshipFriendlyName !== null + ); + } + + async hasOrganizations(userId?: string): Promise { + const organizations = await this.getAll(userId); + return organizations.length > 0; + } +} diff --git a/jslib/common/src/services/passwordGeneration.service.ts b/jslib/common/src/services/passwordGeneration.service.ts new file mode 100644 index 00000000..c752fcd9 --- /dev/null +++ b/jslib/common/src/services/passwordGeneration.service.ts @@ -0,0 +1,572 @@ +import * as zxcvbn from "zxcvbn"; + +import { CryptoService } from "../abstractions/crypto.service"; +import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "../abstractions/passwordGeneration.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { StateService } from "../abstractions/state.service"; +import { PolicyType } from "../enums/policyType"; +import { EEFLongWordList } from "../misc/wordlist"; +import { EncString } from "../models/domain/encString"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { PasswordGeneratorPolicyOptions } from "../models/domain/passwordGeneratorPolicyOptions"; +import { Policy } from "../models/domain/policy"; + +const DefaultOptions = { + length: 14, + ambiguous: false, + number: true, + minNumber: 1, + uppercase: true, + minUppercase: 0, + lowercase: true, + minLowercase: 0, + special: false, + minSpecial: 1, + type: "password", + numWords: 3, + wordSeparator: "-", + capitalize: false, + includeNumber: false, +}; + +const MaxPasswordsInHistory = 100; + +export class PasswordGenerationService implements PasswordGenerationServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private policyService: PolicyService, + private stateService: StateService + ) {} + + async generatePassword(options: any): Promise { + // overload defaults with given options + const o = Object.assign({}, DefaultOptions, options); + + if (o.type === "passphrase") { + return this.generatePassphrase(options); + } + + // sanitize + this.sanitizePasswordLength(o, true); + + const minLength: number = o.minUppercase + o.minLowercase + o.minNumber + o.minSpecial; + if (o.length < minLength) { + o.length = minLength; + } + + const positions: string[] = []; + if (o.lowercase && o.minLowercase > 0) { + for (let i = 0; i < o.minLowercase; i++) { + positions.push("l"); + } + } + if (o.uppercase && o.minUppercase > 0) { + for (let i = 0; i < o.minUppercase; i++) { + positions.push("u"); + } + } + if (o.number && o.minNumber > 0) { + for (let i = 0; i < o.minNumber; i++) { + positions.push("n"); + } + } + if (o.special && o.minSpecial > 0) { + for (let i = 0; i < o.minSpecial; i++) { + positions.push("s"); + } + } + while (positions.length < o.length) { + positions.push("a"); + } + + // shuffle + await this.shuffleArray(positions); + + // build out the char sets + let allCharSet = ""; + + let lowercaseCharSet = "abcdefghijkmnopqrstuvwxyz"; + if (o.ambiguous) { + lowercaseCharSet += "l"; + } + if (o.lowercase) { + allCharSet += lowercaseCharSet; + } + + let uppercaseCharSet = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + if (o.ambiguous) { + uppercaseCharSet += "IO"; + } + if (o.uppercase) { + allCharSet += uppercaseCharSet; + } + + let numberCharSet = "23456789"; + if (o.ambiguous) { + numberCharSet += "01"; + } + if (o.number) { + allCharSet += numberCharSet; + } + + const specialCharSet = "!@#$%^&*"; + if (o.special) { + allCharSet += specialCharSet; + } + + let password = ""; + for (let i = 0; i < o.length; i++) { + let positionChars: string; + switch (positions[i]) { + case "l": + positionChars = lowercaseCharSet; + break; + case "u": + positionChars = uppercaseCharSet; + break; + case "n": + positionChars = numberCharSet; + break; + case "s": + positionChars = specialCharSet; + break; + case "a": + positionChars = allCharSet; + break; + default: + break; + } + + const randomCharIndex = await this.cryptoService.randomNumber(0, positionChars.length - 1); + password += positionChars.charAt(randomCharIndex); + } + + return password; + } + + async generatePassphrase(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.numWords == null || o.numWords <= 2) { + o.numWords = DefaultOptions.numWords; + } + if (o.wordSeparator == null || o.wordSeparator.length === 0 || o.wordSeparator.length > 1) { + o.wordSeparator = " "; + } + if (o.capitalize == null) { + o.capitalize = false; + } + if (o.includeNumber == null) { + o.includeNumber = false; + } + + const listLength = EEFLongWordList.length - 1; + const wordList = new Array(o.numWords); + for (let i = 0; i < o.numWords; i++) { + const wordIndex = await this.cryptoService.randomNumber(0, listLength); + if (o.capitalize) { + wordList[i] = this.capitalize(EEFLongWordList[wordIndex]); + } else { + wordList[i] = EEFLongWordList[wordIndex]; + } + } + + if (o.includeNumber) { + await this.appendRandomNumberToRandomWord(wordList); + } + return wordList.join(o.wordSeparator); + } + + async getOptions(): Promise<[any, PasswordGeneratorPolicyOptions]> { + let options = await this.stateService.getPasswordGenerationOptions(); + if (options == null) { + options = Object.assign({}, DefaultOptions); + } else { + options = Object.assign({}, DefaultOptions, options); + } + await this.stateService.setPasswordGenerationOptions(options); + const enforcedOptions = await this.enforcePasswordGeneratorPoliciesOnOptions(options); + options = enforcedOptions[0]; + return [options, enforcedOptions[1]]; + } + + async enforcePasswordGeneratorPoliciesOnOptions( + options: any + ): Promise<[any, PasswordGeneratorPolicyOptions]> { + let enforcedPolicyOptions = await this.getPasswordGeneratorPolicyOptions(); + if (enforcedPolicyOptions != null) { + if (options.length < enforcedPolicyOptions.minLength) { + options.length = enforcedPolicyOptions.minLength; + } + + if (enforcedPolicyOptions.useUppercase) { + options.uppercase = true; + } + + if (enforcedPolicyOptions.useLowercase) { + options.lowercase = true; + } + + if (enforcedPolicyOptions.useNumbers) { + options.number = true; + } + + if (options.minNumber < enforcedPolicyOptions.numberCount) { + options.minNumber = enforcedPolicyOptions.numberCount; + } + + if (enforcedPolicyOptions.useSpecial) { + options.special = true; + } + + if (options.minSpecial < enforcedPolicyOptions.specialCount) { + options.minSpecial = enforcedPolicyOptions.specialCount; + } + + // Must normalize these fields because the receiving call expects all options to pass the current rules + if (options.minSpecial + options.minNumber > options.length) { + options.minSpecial = options.length - options.minNumber; + } + + if (options.numWords < enforcedPolicyOptions.minNumberWords) { + options.numWords = enforcedPolicyOptions.minNumberWords; + } + + if (enforcedPolicyOptions.capitalize) { + options.capitalize = true; + } + + if (enforcedPolicyOptions.includeNumber) { + options.includeNumber = true; + } + + // Force default type if password/passphrase selected via policy + if ( + enforcedPolicyOptions.defaultType === "password" || + enforcedPolicyOptions.defaultType === "passphrase" + ) { + options.type = enforcedPolicyOptions.defaultType; + } + } else { + // UI layer expects an instantiated object to prevent more explicit null checks + enforcedPolicyOptions = new PasswordGeneratorPolicyOptions(); + } + return [options, enforcedPolicyOptions]; + } + + async getPasswordGeneratorPolicyOptions(): Promise { + const policies: Policy[] = + this.policyService == null + ? null + : await this.policyService.getAll(PolicyType.PasswordGenerator); + let enforcedOptions: PasswordGeneratorPolicyOptions = null; + + if (policies == null || policies.length === 0) { + return enforcedOptions; + } + + policies.forEach((currentPolicy) => { + if (!currentPolicy.enabled || currentPolicy.data == null) { + return; + } + + if (enforcedOptions == null) { + enforcedOptions = new PasswordGeneratorPolicyOptions(); + } + + // Password wins in multi-org collisions + if (currentPolicy.data.defaultType != null && enforcedOptions.defaultType !== "password") { + enforcedOptions.defaultType = currentPolicy.data.defaultType; + } + + if ( + currentPolicy.data.minLength != null && + currentPolicy.data.minLength > enforcedOptions.minLength + ) { + enforcedOptions.minLength = currentPolicy.data.minLength; + } + + if (currentPolicy.data.useUpper) { + enforcedOptions.useUppercase = true; + } + + if (currentPolicy.data.useLower) { + enforcedOptions.useLowercase = true; + } + + if (currentPolicy.data.useNumbers) { + enforcedOptions.useNumbers = true; + } + + if ( + currentPolicy.data.minNumbers != null && + currentPolicy.data.minNumbers > enforcedOptions.numberCount + ) { + enforcedOptions.numberCount = currentPolicy.data.minNumbers; + } + + if (currentPolicy.data.useSpecial) { + enforcedOptions.useSpecial = true; + } + + if ( + currentPolicy.data.minSpecial != null && + currentPolicy.data.minSpecial > enforcedOptions.specialCount + ) { + enforcedOptions.specialCount = currentPolicy.data.minSpecial; + } + + if ( + currentPolicy.data.minNumberWords != null && + currentPolicy.data.minNumberWords > enforcedOptions.minNumberWords + ) { + enforcedOptions.minNumberWords = currentPolicy.data.minNumberWords; + } + + if (currentPolicy.data.capitalize) { + enforcedOptions.capitalize = true; + } + + if (currentPolicy.data.includeNumber) { + enforcedOptions.includeNumber = true; + } + }); + + return enforcedOptions; + } + + async saveOptions(options: any) { + await this.stateService.setPasswordGenerationOptions(options); + } + + async getHistory(): Promise { + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + return new Array(); + } + + if ((await this.stateService.getDecryptedPasswordGenerationHistory()) == null) { + const encrypted = await this.stateService.getEncryptedPasswordGenerationHistory(); + const decrypted = await this.decryptHistory(encrypted); + await this.stateService.setDecryptedPasswordGenerationHistory(decrypted); + } + + const passwordGenerationHistory = + await this.stateService.getDecryptedPasswordGenerationHistory(); + return passwordGenerationHistory != null + ? passwordGenerationHistory + : new Array(); + } + + async addHistory(password: string): Promise { + // Cannot add new history if no key is available + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + return; + } + + const currentHistory = await this.getHistory(); + + // Prevent duplicates + if (this.matchesPrevious(password, currentHistory)) { + return; + } + + currentHistory.unshift(new GeneratedPasswordHistory(password, Date.now())); + + // Remove old items. + if (currentHistory.length > MaxPasswordsInHistory) { + currentHistory.pop(); + } + + const newHistory = await this.encryptHistory(currentHistory); + return await this.stateService.setEncryptedPasswordGenerationHistory(newHistory); + } + + async clear(userId?: string): Promise { + await this.stateService.setEncryptedPasswordGenerationHistory(null, { userId: userId }); + await this.stateService.setDecryptedPasswordGenerationHistory(null, { userId: userId }); + } + + passwordStrength(password: string, userInputs: string[] = null): zxcvbn.ZXCVBNResult { + if (password == null || password.length === 0) { + return null; + } + let globalUserInputs = ["bitwarden", "bit", "warden"]; + if (userInputs != null && userInputs.length > 0) { + globalUserInputs = globalUserInputs.concat(userInputs); + } + // Use a hash set to get rid of any duplicate user inputs + const finalUserInputs = Array.from(new Set(globalUserInputs)); + const result = zxcvbn(password, finalUserInputs); + return result; + } + + normalizeOptions(options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) { + options.minLowercase = 0; + options.minUppercase = 0; + + if (!options.length || options.length < 5) { + options.length = 5; + } else if (options.length > 128) { + options.length = 128; + } + + if (options.length < enforcedPolicyOptions.minLength) { + options.length = enforcedPolicyOptions.minLength; + } + + if (!options.minNumber) { + options.minNumber = 0; + } else if (options.minNumber > options.length) { + options.minNumber = options.length; + } else if (options.minNumber > 9) { + options.minNumber = 9; + } + + if (options.minNumber < enforcedPolicyOptions.numberCount) { + options.minNumber = enforcedPolicyOptions.numberCount; + } + + if (!options.minSpecial) { + options.minSpecial = 0; + } else if (options.minSpecial > options.length) { + options.minSpecial = options.length; + } else if (options.minSpecial > 9) { + options.minSpecial = 9; + } + + if (options.minSpecial < enforcedPolicyOptions.specialCount) { + options.minSpecial = enforcedPolicyOptions.specialCount; + } + + if (options.minSpecial + options.minNumber > options.length) { + options.minSpecial = options.length - options.minNumber; + } + + if (options.numWords == null || options.length < 3) { + options.numWords = 3; + } else if (options.numWords > 20) { + options.numWords = 20; + } + + if (options.numWords < enforcedPolicyOptions.minNumberWords) { + options.numWords = enforcedPolicyOptions.minNumberWords; + } + + if (options.wordSeparator != null && options.wordSeparator.length > 1) { + options.wordSeparator = options.wordSeparator[0]; + } + + this.sanitizePasswordLength(options, false); + } + + private capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + private async appendRandomNumberToRandomWord(wordList: string[]) { + if (wordList == null || wordList.length <= 0) { + return; + } + const index = await this.cryptoService.randomNumber(0, wordList.length - 1); + const num = await this.cryptoService.randomNumber(0, 9); + wordList[index] = wordList[index] + num; + } + + private async encryptHistory( + history: GeneratedPasswordHistory[] + ): Promise { + if (history == null || history.length === 0) { + return Promise.resolve([]); + } + + const promises = history.map(async (item) => { + const encrypted = await this.cryptoService.encrypt(item.password); + return new GeneratedPasswordHistory(encrypted.encryptedString, item.date); + }); + + return await Promise.all(promises); + } + + private async decryptHistory( + history: GeneratedPasswordHistory[] + ): Promise { + if (history == null || history.length === 0) { + return Promise.resolve([]); + } + + const promises = history.map(async (item) => { + const decrypted = await this.cryptoService.decryptToUtf8(new EncString(item.password)); + return new GeneratedPasswordHistory(decrypted, item.date); + }); + + return await Promise.all(promises); + } + + private matchesPrevious(password: string, history: GeneratedPasswordHistory[]): boolean { + if (history == null || history.length === 0) { + return false; + } + + return history[history.length - 1].password === password; + } + + // ref: https://stackoverflow.com/a/12646864/1090359 + private async shuffleArray(array: string[]) { + for (let i = array.length - 1; i > 0; i--) { + const j = await this.cryptoService.randomNumber(0, i); + [array[i], array[j]] = [array[j], array[i]]; + } + } + + private sanitizePasswordLength(options: any, forGeneration: boolean) { + let minUppercaseCalc = 0; + let minLowercaseCalc = 0; + let minNumberCalc: number = options.minNumber; + let minSpecialCalc: number = options.minSpecial; + + if (options.uppercase && options.minUppercase <= 0) { + minUppercaseCalc = 1; + } else if (!options.uppercase) { + minUppercaseCalc = 0; + } + + if (options.lowercase && options.minLowercase <= 0) { + minLowercaseCalc = 1; + } else if (!options.lowercase) { + minLowercaseCalc = 0; + } + + if (options.number && options.minNumber <= 0) { + minNumberCalc = 1; + } else if (!options.number) { + minNumberCalc = 0; + } + + if (options.special && options.minSpecial <= 0) { + minSpecialCalc = 1; + } else if (!options.special) { + minSpecialCalc = 0; + } + + // This should never happen but is a final safety net + if (!options.length || options.length < 1) { + options.length = 10; + } + + const minLength: number = minUppercaseCalc + minLowercaseCalc + minNumberCalc + minSpecialCalc; + // Normalize and Generation both require this modification + if (options.length < minLength) { + options.length = minLength; + } + + // Apply other changes if the options object passed in is for generation + if (forGeneration) { + options.minUppercase = minUppercaseCalc; + options.minLowercase = minLowercaseCalc; + options.minNumber = minNumberCalc; + options.minSpecial = minSpecialCalc; + } + } +} diff --git a/jslib/common/src/services/policy.service.ts b/jslib/common/src/services/policy.service.ts new file mode 100644 index 00000000..91ab864b --- /dev/null +++ b/jslib/common/src/services/policy.service.ts @@ -0,0 +1,247 @@ +import { ApiService } from "../abstractions/api.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { PolicyService as PolicyServiceAbstraction } from "../abstractions/policy.service"; +import { StateService } from "../abstractions/state.service"; +import { OrganizationUserStatusType } from "../enums/organizationUserStatusType"; +import { OrganizationUserType } from "../enums/organizationUserType"; +import { PolicyType } from "../enums/policyType"; +import { PolicyData } from "../models/data/policyData"; +import { MasterPasswordPolicyOptions } from "../models/domain/masterPasswordPolicyOptions"; +import { Organization } from "../models/domain/organization"; +import { Policy } from "../models/domain/policy"; +import { ResetPasswordPolicyOptions } from "../models/domain/resetPasswordPolicyOptions"; +import { ListResponse } from "../models/response/listResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; + +export class PolicyService implements PolicyServiceAbstraction { + policyCache: Policy[]; + + constructor( + private stateService: StateService, + private organizationService: OrganizationService, + private apiService: ApiService + ) {} + + async clearCache(): Promise { + await this.stateService.setDecryptedPolicies(null); + } + + async getAll(type?: PolicyType, userId?: string): Promise { + let response: Policy[] = []; + const decryptedPolicies = await this.stateService.getDecryptedPolicies({ userId: userId }); + if (decryptedPolicies != null) { + response = decryptedPolicies; + } else { + const diskPolicies = await this.stateService.getEncryptedPolicies({ userId: userId }); + for (const id in diskPolicies) { + // eslint-disable-next-line + if (diskPolicies.hasOwnProperty(id)) { + response.push(new Policy(diskPolicies[id])); + } + } + await this.stateService.setDecryptedPolicies(response, { userId: userId }); + } + if (type != null) { + return response.filter((policy) => policy.type === type); + } else { + return response; + } + } + + async getPolicyForOrganization(policyType: PolicyType, organizationId: string): Promise { + const org = await this.organizationService.get(organizationId); + if (org?.isProviderUser) { + const orgPolicies = await this.apiService.getPolicies(organizationId); + const policy = orgPolicies.data.find((p) => p.organizationId === organizationId); + + if (policy == null) { + return null; + } + + return new Policy(new PolicyData(policy)); + } + + const policies = await this.getAll(policyType); + return policies.find((p) => p.organizationId === organizationId); + } + + async replace(policies: { [id: string]: PolicyData }): Promise { + await this.stateService.setDecryptedPolicies(null); + await this.stateService.setEncryptedPolicies(policies); + } + + async clear(userId?: string): Promise { + await this.stateService.setDecryptedPolicies(null, { userId: userId }); + await this.stateService.setEncryptedPolicies(null, { userId: userId }); + } + + async getMasterPasswordPoliciesForInvitedUsers( + orgId: string + ): Promise { + const userId = await this.stateService.getUserId(); + const response = await this.apiService.getPoliciesByInvitedUser(orgId, userId); + const policies = await this.mapPoliciesFromToken(response); + return this.getMasterPasswordPolicyOptions(policies); + } + + async getMasterPasswordPolicyOptions(policies?: Policy[]): Promise { + let enforcedOptions: MasterPasswordPolicyOptions = null; + + if (policies == null) { + policies = await this.getAll(PolicyType.MasterPassword); + } else { + policies = policies.filter((p) => p.type === PolicyType.MasterPassword); + } + + if (policies == null || policies.length === 0) { + return enforcedOptions; + } + + policies.forEach((currentPolicy) => { + if (!currentPolicy.enabled || currentPolicy.data == null) { + return; + } + + if (enforcedOptions == null) { + enforcedOptions = new MasterPasswordPolicyOptions(); + } + + if ( + currentPolicy.data.minComplexity != null && + currentPolicy.data.minComplexity > enforcedOptions.minComplexity + ) { + enforcedOptions.minComplexity = currentPolicy.data.minComplexity; + } + + if ( + currentPolicy.data.minLength != null && + currentPolicy.data.minLength > enforcedOptions.minLength + ) { + enforcedOptions.minLength = currentPolicy.data.minLength; + } + + if (currentPolicy.data.requireUpper) { + enforcedOptions.requireUpper = true; + } + + if (currentPolicy.data.requireLower) { + enforcedOptions.requireLower = true; + } + + if (currentPolicy.data.requireNumbers) { + enforcedOptions.requireNumbers = true; + } + + if (currentPolicy.data.requireSpecial) { + enforcedOptions.requireSpecial = true; + } + }); + + return enforcedOptions; + } + + evaluateMasterPassword( + passwordStrength: number, + newPassword: string, + enforcedPolicyOptions: MasterPasswordPolicyOptions + ): boolean { + if (enforcedPolicyOptions == null) { + return true; + } + + if ( + enforcedPolicyOptions.minComplexity > 0 && + enforcedPolicyOptions.minComplexity > passwordStrength + ) { + return false; + } + + if ( + enforcedPolicyOptions.minLength > 0 && + enforcedPolicyOptions.minLength > newPassword.length + ) { + return false; + } + + if (enforcedPolicyOptions.requireUpper && newPassword.toLocaleLowerCase() === newPassword) { + return false; + } + + if (enforcedPolicyOptions.requireLower && newPassword.toLocaleUpperCase() === newPassword) { + return false; + } + + if (enforcedPolicyOptions.requireNumbers && !/[0-9]/.test(newPassword)) { + return false; + } + + // eslint-disable-next-line + if (enforcedPolicyOptions.requireSpecial && !/[!@#$%\^&*]/g.test(newPassword)) { + return false; + } + + return true; + } + + getResetPasswordPolicyOptions( + policies: Policy[], + orgId: string + ): [ResetPasswordPolicyOptions, boolean] { + const resetPasswordPolicyOptions = new ResetPasswordPolicyOptions(); + + if (policies == null || orgId == null) { + return [resetPasswordPolicyOptions, false]; + } + + const policy = policies.find( + (p) => p.organizationId === orgId && p.type === PolicyType.ResetPassword && p.enabled + ); + resetPasswordPolicyOptions.autoEnrollEnabled = policy?.data?.autoEnrollEnabled ?? false; + + return [resetPasswordPolicyOptions, policy?.enabled ?? false]; + } + + mapPoliciesFromToken(policiesResponse: ListResponse): Policy[] { + if (policiesResponse == null || policiesResponse.data == null) { + return null; + } + + const policiesData = policiesResponse.data.map((p) => new PolicyData(p)); + return policiesData.map((p) => new Policy(p)); + } + + async policyAppliesToUser( + policyType: PolicyType, + policyFilter?: (policy: Policy) => boolean, + userId?: string + ) { + const policies = await this.getAll(policyType, userId); + const organizations = await this.organizationService.getAll(userId); + let filteredPolicies; + + if (policyFilter != null) { + filteredPolicies = policies.filter((p) => p.enabled && policyFilter(p)); + } else { + filteredPolicies = policies.filter((p) => p.enabled); + } + + const policySet = new Set(filteredPolicies.map((p) => p.organizationId)); + + return organizations.some( + (o) => + o.enabled && + o.status >= OrganizationUserStatusType.Accepted && + o.usePolicies && + !this.isExcemptFromPolicies(o, policyType) && + policySet.has(o.id) + ); + } + + private isExcemptFromPolicies(organization: Organization, policyType: PolicyType) { + if (policyType === PolicyType.MaximumVaultTimeout) { + return organization.type === OrganizationUserType.Owner; + } + + return organization.isExemptFromPolicies; + } +} diff --git a/jslib/common/src/services/provider.service.ts b/jslib/common/src/services/provider.service.ts new file mode 100644 index 00000000..53f09542 --- /dev/null +++ b/jslib/common/src/services/provider.service.ts @@ -0,0 +1,34 @@ +import { ProviderService as ProviderServiceAbstraction } from "../abstractions/provider.service"; +import { StateService } from "../abstractions/state.service"; +import { ProviderData } from "../models/data/providerData"; +import { Provider } from "../models/domain/provider"; + +export class ProviderService implements ProviderServiceAbstraction { + constructor(private stateService: StateService) {} + + async get(id: string): Promise { + const providers = await this.stateService.getProviders(); + // eslint-disable-next-line + if (providers == null || !providers.hasOwnProperty(id)) { + return null; + } + + return new Provider(providers[id]); + } + + async getAll(): Promise { + const providers = await this.stateService.getProviders(); + const response: Provider[] = []; + for (const id in providers) { + // eslint-disable-next-line + if (providers.hasOwnProperty(id)) { + response.push(new Provider(providers[id])); + } + } + return response; + } + + async save(providers: { [id: string]: ProviderData }) { + await this.stateService.setProviders(providers); + } +} diff --git a/jslib/common/src/services/search.service.ts b/jslib/common/src/services/search.service.ts new file mode 100644 index 00000000..e1c54ceb --- /dev/null +++ b/jslib/common/src/services/search.service.ts @@ -0,0 +1,284 @@ +import * as lunr from "lunr"; + +import { CipherService } from "../abstractions/cipher.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { LogService } from "../abstractions/log.service"; +import { SearchService as SearchServiceAbstraction } from "../abstractions/search.service"; +import { CipherType } from "../enums/cipherType"; +import { FieldType } from "../enums/fieldType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { CipherView } from "../models/view/cipherView"; +import { SendView } from "../models/view/sendView"; + +export class SearchService implements SearchServiceAbstraction { + indexedEntityId?: string = null; + private indexing = false; + private index: lunr.Index = null; + private searchableMinLength = 2; + + constructor( + private cipherService: CipherService, + private logService: LogService, + private i18nService: I18nService + ) { + if (["zh-CN", "zh-TW"].indexOf(i18nService.locale) !== -1) { + this.searchableMinLength = 1; + } + } + + clearIndex(): void { + this.indexedEntityId = null; + this.index = null; + } + + isSearchable(query: string): boolean { + const notSearchable = + query == null || + (this.index == null && query.length < this.searchableMinLength) || + (this.index != null && query.length < this.searchableMinLength && query.indexOf(">") !== 0); + return !notSearchable; + } + + async indexCiphers(indexedEntityId?: string, ciphers?: CipherView[]): Promise { + if (this.indexing) { + return; + } + + this.logService.time("search indexing"); + this.indexing = true; + this.indexedEntityId = indexedEntityId; + this.index = null; + const builder = new lunr.Builder(); + builder.ref("id"); + builder.field("shortid", { boost: 100, extractor: (c: CipherView) => c.id.substr(0, 8) }); + builder.field("name", { boost: 10 }); + builder.field("subtitle", { + boost: 5, + extractor: (c: CipherView) => { + if (c.subTitle != null && c.type === CipherType.Card) { + return c.subTitle.replace(/\*/g, ""); + } + return c.subTitle; + }, + }); + builder.field("notes"); + builder.field("login.username", { + extractor: (c: CipherView) => + c.type === CipherType.Login && c.login != null ? c.login.username : null, + }); + builder.field("login.uris", { boost: 2, extractor: (c: CipherView) => this.uriExtractor(c) }); + builder.field("fields", { extractor: (c: CipherView) => this.fieldExtractor(c, false) }); + builder.field("fields_joined", { extractor: (c: CipherView) => this.fieldExtractor(c, true) }); + builder.field("attachments", { + extractor: (c: CipherView) => this.attachmentExtractor(c, false), + }); + builder.field("attachments_joined", { + extractor: (c: CipherView) => this.attachmentExtractor(c, true), + }); + builder.field("organizationid", { extractor: (c: CipherView) => c.organizationId }); + ciphers = ciphers || (await this.cipherService.getAllDecrypted()); + ciphers.forEach((c) => builder.add(c)); + this.index = builder.build(); + + this.indexing = false; + + this.logService.timeEnd("search indexing"); + } + + async searchCiphers( + query: string, + filter: ((cipher: CipherView) => boolean) | ((cipher: CipherView) => boolean)[] = null, + ciphers: CipherView[] = null + ): Promise { + const results: CipherView[] = []; + if (query != null) { + query = query.trim().toLowerCase(); + } + if (query === "") { + query = null; + } + + if (ciphers == null) { + ciphers = await this.cipherService.getAllDecrypted(); + } + + if (filter != null && Array.isArray(filter) && filter.length > 0) { + ciphers = ciphers.filter((c) => filter.every((f) => f == null || f(c))); + } else if (filter != null) { + ciphers = ciphers.filter(filter as (cipher: CipherView) => boolean); + } + + if (!this.isSearchable(query)) { + return ciphers; + } + + if (this.indexing) { + await new Promise((r) => setTimeout(r, 250)); + if (this.indexing) { + await new Promise((r) => setTimeout(r, 500)); + } + } + + const index = this.getIndexForSearch(); + if (index == null) { + // Fall back to basic search if index is not available + return this.searchCiphersBasic(ciphers, query); + } + + const ciphersMap = new Map(); + ciphers.forEach((c) => ciphersMap.set(c.id, c)); + + let searchResults: lunr.Index.Result[] = null; + const isQueryString = query != null && query.length > 1 && query.indexOf(">") === 0; + if (isQueryString) { + try { + searchResults = index.search(query.substr(1).trim()); + } catch (e) { + this.logService.error(e); + } + } else { + const soWild = lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING; + searchResults = index.query((q) => { + lunr.tokenizer(query).forEach((token) => { + const t = token.toString(); + q.term(t, { fields: ["name"], wildcard: soWild }); + q.term(t, { fields: ["subtitle"], wildcard: soWild }); + q.term(t, { fields: ["login.uris"], wildcard: soWild }); + q.term(t, {}); + }); + }); + } + + if (searchResults != null) { + searchResults.forEach((r) => { + if (ciphersMap.has(r.ref)) { + results.push(ciphersMap.get(r.ref)); + } + }); + } + return results; + } + + searchCiphersBasic(ciphers: CipherView[], query: string, deleted = false) { + query = query.trim().toLowerCase(); + return ciphers.filter((c) => { + if (deleted !== c.isDeleted) { + return false; + } + if (c.name != null && c.name.toLowerCase().indexOf(query) > -1) { + return true; + } + if (query.length >= 8 && c.id.startsWith(query)) { + return true; + } + if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(query) > -1) { + return true; + } + if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(query) > -1) { + return true; + } + return false; + }); + } + + searchSends(sends: SendView[], query: string) { + query = query.trim().toLocaleLowerCase(); + + return sends.filter((s) => { + if (s.name != null && s.name.toLowerCase().indexOf(query) > -1) { + return true; + } + if ( + query.length >= 8 && + (s.id.startsWith(query) || + s.accessId.toLocaleLowerCase().startsWith(query) || + (s.file?.id != null && s.file.id.startsWith(query))) + ) { + return true; + } + if (s.notes != null && s.notes.toLowerCase().indexOf(query) > -1) { + return true; + } + if (s.text?.text != null && s.text.text.toLowerCase().indexOf(query) > -1) { + return true; + } + if (s.file?.fileName != null && s.file.fileName.toLowerCase().indexOf(query) > -1) { + return true; + } + }); + } + + getIndexForSearch(): lunr.Index { + return this.index; + } + + private fieldExtractor(c: CipherView, joined: boolean) { + if (!c.hasFields) { + return null; + } + let fields: string[] = []; + c.fields.forEach((f) => { + if (f.name != null) { + fields.push(f.name); + } + if (f.type === FieldType.Text && f.value != null) { + fields.push(f.value); + } + }); + fields = fields.filter((f) => f.trim() !== ""); + if (fields.length === 0) { + return null; + } + return joined ? fields.join(" ") : fields; + } + + private attachmentExtractor(c: CipherView, joined: boolean) { + if (!c.hasAttachments) { + return null; + } + let attachments: string[] = []; + c.attachments.forEach((a) => { + if (a != null && a.fileName != null) { + if (joined && a.fileName.indexOf(".") > -1) { + attachments.push(a.fileName.substr(0, a.fileName.lastIndexOf("."))); + } else { + attachments.push(a.fileName); + } + } + }); + attachments = attachments.filter((f) => f.trim() !== ""); + if (attachments.length === 0) { + return null; + } + return joined ? attachments.join(" ") : attachments; + } + + private uriExtractor(c: CipherView) { + if (c.type !== CipherType.Login || c.login == null || !c.login.hasUris) { + return null; + } + const uris: string[] = []; + c.login.uris.forEach((u) => { + if (u.uri == null || u.uri === "") { + return; + } + if (u.hostname != null) { + uris.push(u.hostname); + return; + } + let uri = u.uri; + if (u.match !== UriMatchType.RegularExpression) { + const protocolIndex = uri.indexOf("://"); + if (protocolIndex > -1) { + uri = uri.substr(protocolIndex + 3); + } + const queryIndex = uri.search(/\?|&|#/); + if (queryIndex > -1) { + uri = uri.substring(0, queryIndex); + } + } + uris.push(uri); + }); + return uris.length > 0 ? uris : null; + } +} diff --git a/jslib/common/src/services/send.service.ts b/jslib/common/src/services/send.service.ts new file mode 100644 index 00000000..75530db3 --- /dev/null +++ b/jslib/common/src/services/send.service.ts @@ -0,0 +1,297 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { FileUploadService } from "../abstractions/fileUpload.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { SendService as SendServiceAbstraction } from "../abstractions/send.service"; +import { StateService } from "../abstractions/state.service"; +import { SEND_KDF_ITERATIONS } from "../enums/kdfType"; +import { SendType } from "../enums/sendType"; +import { Utils } from "../misc/utils"; +import { SendData } from "../models/data/sendData"; +import { EncArrayBuffer } from "../models/domain/encArrayBuffer"; +import { EncString } from "../models/domain/encString"; +import { Send } from "../models/domain/send"; +import { SendFile } from "../models/domain/sendFile"; +import { SendText } from "../models/domain/sendText"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { SendRequest } from "../models/request/sendRequest"; +import { ErrorResponse } from "../models/response/errorResponse"; +import { SendResponse } from "../models/response/sendResponse"; +import { SendView } from "../models/view/sendView"; + +export class SendService implements SendServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private apiService: ApiService, + private fileUploadService: FileUploadService, + private i18nService: I18nService, + private cryptoFunctionService: CryptoFunctionService, + private stateService: StateService + ) {} + + async clearCache(): Promise { + await this.stateService.setDecryptedSends(null); + } + + async encrypt( + model: SendView, + file: File | ArrayBuffer, + password: string, + key?: SymmetricCryptoKey + ): Promise<[Send, EncArrayBuffer]> { + let fileData: EncArrayBuffer = null; + const send = new Send(); + send.id = model.id; + send.type = model.type; + send.disabled = model.disabled; + send.hideEmail = model.hideEmail; + send.maxAccessCount = model.maxAccessCount; + if (model.key == null) { + model.key = await this.cryptoFunctionService.randomBytes(16); + model.cryptoKey = await this.cryptoService.makeSendKey(model.key); + } + if (password != null) { + const passwordHash = await this.cryptoFunctionService.pbkdf2( + password, + model.key, + "sha256", + SEND_KDF_ITERATIONS + ); + send.password = Utils.fromBufferToB64(passwordHash); + } + send.key = await this.cryptoService.encrypt(model.key, key); + send.name = await this.cryptoService.encrypt(model.name, model.cryptoKey); + send.notes = await this.cryptoService.encrypt(model.notes, model.cryptoKey); + if (send.type === SendType.Text) { + send.text = new SendText(); + send.text.text = await this.cryptoService.encrypt(model.text.text, model.cryptoKey); + send.text.hidden = model.text.hidden; + } else if (send.type === SendType.File) { + send.file = new SendFile(); + if (file != null) { + if (file instanceof ArrayBuffer) { + const [name, data] = await this.encryptFileData( + model.file.fileName, + file, + model.cryptoKey + ); + send.file.fileName = name; + fileData = data; + } else { + fileData = await this.parseFile(send, file, model.cryptoKey); + } + } + } + + return [send, fileData]; + } + + async get(id: string): Promise { + const sends = await this.stateService.getEncryptedSends(); + // eslint-disable-next-line + if (sends == null || !sends.hasOwnProperty(id)) { + return null; + } + + return new Send(sends[id]); + } + + async getAll(): Promise { + const sends = await this.stateService.getEncryptedSends(); + const response: Send[] = []; + for (const id in sends) { + // eslint-disable-next-line + if (sends.hasOwnProperty(id)) { + response.push(new Send(sends[id])); + } + } + return response; + } + + async getAllDecrypted(): Promise { + let decSends = await this.stateService.getDecryptedSends(); + if (decSends != null) { + return decSends; + } + + decSends = []; + const hasKey = await this.cryptoService.hasKey(); + if (!hasKey) { + throw new Error("No key."); + } + + const promises: Promise[] = []; + const sends = await this.getAll(); + sends.forEach((send) => { + promises.push(send.decrypt().then((f) => decSends.push(f))); + }); + + await Promise.all(promises); + decSends.sort(Utils.getSortFunction(this.i18nService, "name")); + + await this.stateService.setDecryptedSends(decSends); + return decSends; + } + + async saveWithServer(sendData: [Send, EncArrayBuffer]): Promise { + const request = new SendRequest(sendData[0], sendData[1]?.buffer.byteLength); + let response: SendResponse; + if (sendData[0].id == null) { + if (sendData[0].type === SendType.Text) { + response = await this.apiService.postSend(request); + } else { + try { + const uploadDataResponse = await this.apiService.postFileTypeSend(request); + response = uploadDataResponse.sendResponse; + + await this.fileUploadService.uploadSendFile( + uploadDataResponse, + sendData[0].file.fileName, + sendData[1] + ); + } catch (e) { + if (e instanceof ErrorResponse && (e as ErrorResponse).statusCode === 404) { + response = await this.legacyServerSendFileUpload(sendData, request); + } else if (e instanceof ErrorResponse) { + throw new Error((e as ErrorResponse).getSingleMessage()); + } else { + throw e; + } + } + } + sendData[0].id = response.id; + sendData[0].accessId = response.accessId; + } else { + response = await this.apiService.putSend(sendData[0].id, request); + } + + const userId = await this.stateService.getUserId(); + const data = new SendData(response, userId); + await this.upsert(data); + } + + /** + * @deprecated Mar 25 2021: This method has been deprecated in favor of direct uploads. + * This method still exists for backward compatibility with old server versions. + */ + async legacyServerSendFileUpload( + sendData: [Send, EncArrayBuffer], + request: SendRequest + ): Promise { + const fd = new FormData(); + try { + const blob = new Blob([sendData[1].buffer], { type: "application/octet-stream" }); + fd.append("model", JSON.stringify(request)); + fd.append("data", blob, sendData[0].file.fileName.encryptedString); + } catch (e) { + if (Utils.isNode && !Utils.isBrowser) { + fd.append("model", JSON.stringify(request)); + fd.append( + "data", + Buffer.from(sendData[1].buffer) as any, + { + filepath: sendData[0].file.fileName.encryptedString, + contentType: "application/octet-stream", + } as any + ); + } else { + throw e; + } + } + return await this.apiService.postSendFileLegacy(fd); + } + + async upsert(send: SendData | SendData[]): Promise { + let sends = await this.stateService.getEncryptedSends(); + if (sends == null) { + sends = {}; + } + + if (send instanceof SendData) { + const s = send as SendData; + sends[s.id] = s; + } else { + (send as SendData[]).forEach((s) => { + sends[s.id] = s; + }); + } + + await this.replace(sends); + } + + async replace(sends: { [id: string]: SendData }): Promise { + await this.stateService.setDecryptedSends(null); + await this.stateService.setEncryptedSends(sends); + } + + async clear(): Promise { + await this.stateService.setDecryptedSends(null); + await this.stateService.setEncryptedSends(null); + } + + async delete(id: string | string[]): Promise { + const sends = await this.stateService.getEncryptedSends(); + if (sends == null) { + return; + } + + if (typeof id === "string") { + if (sends[id] == null) { + return; + } + delete sends[id]; + } else { + (id as string[]).forEach((i) => { + delete sends[i]; + }); + } + + await this.replace(sends); + } + + async deleteWithServer(id: string): Promise { + await this.apiService.deleteSend(id); + await this.delete(id); + } + + async removePasswordWithServer(id: string): Promise { + const response = await this.apiService.putSendRemovePassword(id); + const userId = await this.stateService.getUserId(); + const data = new SendData(response, userId); + await this.upsert(data); + } + + private parseFile(send: Send, file: File, key: SymmetricCryptoKey): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsArrayBuffer(file); + reader.onload = async (evt) => { + try { + const [name, data] = await this.encryptFileData( + file.name, + evt.target.result as ArrayBuffer, + key + ); + send.file.fileName = name; + resolve(data); + } catch (e) { + reject(e); + } + }; + reader.onerror = () => { + reject("Error reading file."); + }; + }); + } + + private async encryptFileData( + fileName: string, + data: ArrayBuffer, + key: SymmetricCryptoKey + ): Promise<[EncString, EncArrayBuffer]> { + const encFileName = await this.cryptoService.encrypt(fileName, key); + const encFileData = await this.cryptoService.encryptToBytes(data, key); + return [encFileName, encFileData]; + } +} diff --git a/jslib/common/src/services/settings.service.ts b/jslib/common/src/services/settings.service.ts new file mode 100644 index 00000000..7f5131b0 --- /dev/null +++ b/jslib/common/src/services/settings.service.ts @@ -0,0 +1,56 @@ +import { SettingsService as SettingsServiceAbstraction } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; + +const Keys = { + settingsPrefix: "settings_", + equivalentDomains: "equivalentDomains", +}; + +export class SettingsService implements SettingsServiceAbstraction { + constructor(private stateService: StateService) {} + + async clearCache(): Promise { + await this.stateService.setSettings(null); + } + + getEquivalentDomains(): Promise { + return this.getSettingsKey(Keys.equivalentDomains); + } + + async setEquivalentDomains(equivalentDomains: string[][]): Promise { + await this.setSettingsKey(Keys.equivalentDomains, equivalentDomains); + } + + async clear(userId?: string): Promise { + await this.stateService.setSettings(null, { userId: userId }); + } + + // Helpers + + private async getSettings(): Promise { + const settings = await this.stateService.getSettings(); + if (settings == null) { + // eslint-disable-next-line + const userId = await this.stateService.getUserId(); + } + return settings; + } + + private async getSettingsKey(key: string): Promise { + const settings = await this.getSettings(); + if (settings != null && settings[key]) { + return settings[key]; + } + return null; + } + + private async setSettingsKey(key: string, value: any): Promise { + let settings = await this.getSettings(); + if (!settings) { + settings = {}; + } + + settings[key] = value; + await this.stateService.setSettings(settings); + } +} diff --git a/jslib/common/src/services/state.service.ts b/jslib/common/src/services/state.service.ts new file mode 100644 index 00000000..39a08f50 --- /dev/null +++ b/jslib/common/src/services/state.service.ts @@ -0,0 +1,2536 @@ +import { BehaviorSubject } from "rxjs"; + +import { LogService } from "../abstractions/log.service"; +import { StateService as StateServiceAbstraction } from "../abstractions/state.service"; +import { StateMigrationService } from "../abstractions/stateMigration.service"; +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { KdfType } from "../enums/kdfType"; +import { StorageLocation } from "../enums/storageLocation"; +import { ThemeType } from "../enums/themeType"; +import { UriMatchType } from "../enums/uriMatchType"; +import { StateFactory } from "../factories/stateFactory"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account, AccountData } from "../models/domain/account"; +import { EncString } from "../models/domain/encString"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { GlobalState } from "../models/domain/globalState"; +import { Policy } from "../models/domain/policy"; +import { State } from "../models/domain/state"; +import { StorageOptions } from "../models/domain/storageOptions"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; +import { WindowState } from "../models/domain/windowState"; +import { CipherView } from "../models/view/cipherView"; +import { CollectionView } from "../models/view/collectionView"; +import { FolderView } from "../models/view/folderView"; +import { SendView } from "../models/view/sendView"; + +const keys = { + global: "global", + authenticatedAccounts: "authenticatedAccounts", + activeUserId: "activeUserId", + tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication + accountActivity: "accountActivity", +}; + +const partialKeys = { + autoKey: "_masterkey_auto", + biometricKey: "_masterkey_biometric", + masterKey: "_masterkey", +}; + +export class StateService< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> implements StateServiceAbstraction +{ + accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({}); + activeAccount = new BehaviorSubject(null); + + protected state: State = new State( + this.createGlobals() + ); + + private hasBeenInited = false; + + private accountDiskCache: Map; + + constructor( + protected storageService: StorageService, + protected secureStorageService: StorageService, + protected logService: LogService, + protected stateMigrationService: StateMigrationService, + protected stateFactory: StateFactory, + protected useAccountCache: boolean = true + ) { + this.accountDiskCache = new Map(); + } + + async init(): Promise { + if (this.hasBeenInited) { + return; + } + + if (await this.stateMigrationService.needsMigration()) { + await this.stateMigrationService.migrate(); + } + + await this.initAccountState(); + this.hasBeenInited = true; + } + + async initAccountState() { + this.state.authenticatedAccounts = + (await this.storageService.get(keys.authenticatedAccounts)) ?? []; + for (const i in this.state.authenticatedAccounts) { + if (i != null) { + await this.syncAccountFromDisk(this.state.authenticatedAccounts[i]); + } + } + const storedActiveUser = await this.storageService.get(keys.activeUserId); + if (storedActiveUser != null) { + this.state.activeUserId = storedActiveUser; + } + await this.pushAccounts(); + this.activeAccount.next(this.state.activeUserId); + } + + async syncAccountFromDisk(userId: string) { + if (userId == null) { + return; + } + this.state.accounts[userId] = this.createAccount(); + const diskAccount = await this.getAccountFromDisk({ userId: userId }); + this.state.accounts[userId].profile = diskAccount.profile; + } + + async addAccount(account: TAccount) { + account = await this.setAccountEnvironmentUrls(account); + this.state.authenticatedAccounts.push(account.profile.userId); + await this.storageService.save(keys.authenticatedAccounts, this.state.authenticatedAccounts); + this.state.accounts[account.profile.userId] = account; + await this.scaffoldNewAccountStorage(account); + await this.setLastActive(new Date().getTime(), { userId: account.profile.userId }); + await this.setActiveUser(account.profile.userId); + this.activeAccount.next(account.profile.userId); + } + + async setActiveUser(userId: string): Promise { + this.clearDecryptedDataForActiveUser(); + this.state.activeUserId = userId; + await this.storageService.save(keys.activeUserId, userId); + this.activeAccount.next(this.state.activeUserId); + await this.pushAccounts(); + } + + async clean(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, this.defaultInMemoryOptions); + await this.deAuthenticateAccount(options.userId); + if (options.userId === this.state.activeUserId) { + await this.dynamicallySetActiveUser(); + } + + await this.removeAccountFromDisk(options?.userId); + this.removeAccountFromMemory(options?.userId); + await this.pushAccounts(); + } + + async getAccessToken(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.tokens?.accessToken; + } + + async setAccessToken(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.tokens.accessToken = value; + await this.saveAccount(account, options); + } + + async getAddEditCipherInfo(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.addEditCipherInfo; + } + + async setAddEditCipherInfo(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.addEditCipherInfo = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getAlwaysShowDock(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.alwaysShowDock ?? false + ); + } + + async setAlwaysShowDock(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.alwaysShowDock = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getApiKeyClientId(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.profile?.apiKeyClientId; + } + + async setApiKeyClientId(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.profile.apiKeyClientId = value; + await this.saveAccount(account, options); + } + + async getApiKeyClientSecret(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.keys?.apiKeyClientSecret; + } + + async setApiKeyClientSecret(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.keys.apiKeyClientSecret = value; + await this.saveAccount(account, options); + } + + async getAutoConfirmFingerPrints(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.autoConfirmFingerPrints ?? false + ); + } + + async setAutoConfirmFingerprints(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.autoConfirmFingerPrints = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getAutoFillOnPageLoadDefault(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.autoFillOnPageLoadDefault ?? true + ); + } + + async setAutoFillOnPageLoadDefault(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.autoFillOnPageLoadDefault = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricAwaitingAcceptance(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.biometricAwaitingAcceptance ?? false + ); + } + + async setBiometricAwaitingAcceptance(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricAwaitingAcceptance = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricFingerprintValidated(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.biometricFingerprintValidated ?? false + ); + } + + async setBiometricFingerprintValidated(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricFingerprintValidated = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricLocked(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.settings + ?.biometricLocked ?? false + ); + } + + async setBiometricLocked(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.settings.biometricLocked = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getBiometricText(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.biometricText; + } + + async setBiometricText(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.biometricText = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getBiometricUnlock(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.biometricUnlock ?? false + ); + } + + async setBiometricUnlock(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.biometricUnlock = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getCanAccessPremium(options?: StorageOptions): Promise { + if (!(await this.getIsAuthenticated(options))) { + return false; + } + + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + if (account.profile.hasPremiumPersonally) { + return true; + } + + const organizations = await this.getOrganizations(options); + if (organizations == null) { + return false; + } + + for (const id of Object.keys(organizations)) { + const o = organizations[id]; + if (o.enabled && o.usersGetPremium && !o.isProviderUser) { + return true; + } + } + + return false; + } + + async getClearClipboard(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.clearClipboard ?? null + ); + } + + async setClearClipboard(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.clearClipboard = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getCollapsedGroupings(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.collapsedGroupings; + } + + async setCollapsedGroupings(value: string[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.collapsedGroupings = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getConvertAccountToKeyConnector(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.convertAccountToKeyConnector; + } + + async setConvertAccountToKeyConnector(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.convertAccountToKeyConnector = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getCryptoMasterKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.cryptoMasterKey; + } + + async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.cryptoMasterKey = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getCryptoMasterKeyAuto(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "auto" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get(`${options.userId}${partialKeys.autoKey}`, options); + } + + async setCryptoMasterKeyAuto(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "auto" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.autoKey, value, options); + } + + async getCryptoMasterKeyB64(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultSecureStorageOptions()); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get( + `${options?.userId}${partialKeys.masterKey}`, + options + ); + } + + async setCryptoMasterKeyB64(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultSecureStorageOptions()); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.masterKey, value, options); + } + + async getCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return null; + } + return await this.secureStorageService.get( + `${options.userId}${partialKeys.biometricKey}`, + options + ); + } + + async hasCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return false; + } + return await this.secureStorageService.has( + `${options.userId}${partialKeys.biometricKey}`, + options + ); + } + + async setCryptoMasterKeyBiometric(value: string, options?: StorageOptions): Promise { + options = this.reconcileOptions( + this.reconcileOptions(options, { keySuffix: "biometric" }), + await this.defaultSecureStorageOptions() + ); + if (options?.userId == null) { + return; + } + await this.saveSecureStorageKey(partialKeys.biometricKey, value, options); + } + + async getDecodedToken(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.tokens?.decodedToken; + } + + async setDecodedToken(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.tokens.decodedToken = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCiphers(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.ciphers?.decrypted; + } + + async setDecryptedCiphers(value: CipherView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.ciphers.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCollections(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.collections?.decrypted; + } + + async setDecryptedCollections(value: CollectionView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.collections.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.cryptoSymmetricKey?.decrypted; + } + + async setDecryptedCryptoSymmetricKey( + value: SymmetricCryptoKey, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.cryptoSymmetricKey.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedFolders(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.folders?.decrypted; + } + + async setDecryptedFolders(value: FolderView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.folders.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedOrganizationKeys( + options?: StorageOptions + ): Promise> { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.organizationKeys?.decrypted; + } + + async setDecryptedOrganizationKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.organizationKeys.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPasswordGenerationHistory( + options?: StorageOptions + ): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.passwordGenerationHistory?.decrypted; + } + + async setDecryptedPasswordGenerationHistory( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.passwordGenerationHistory.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPinProtected(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.settings?.pinProtected?.decrypted; + } + + async setDecryptedPinProtected(value: EncString, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.settings.pinProtected.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPolicies(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.policies?.decrypted; + } + + async setDecryptedPolicies(value: Policy[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.policies.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedPrivateKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.privateKey?.decrypted; + } + + async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.privateKey.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedProviderKeys( + options?: StorageOptions + ): Promise> { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.providerKeys?.decrypted; + } + + async setDecryptedProviderKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.providerKeys.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDecryptedSends(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.data?.sends?.decrypted; + } + + async setDecryptedSends(value: SendView[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.data.sends.decrypted = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getDefaultUriMatch(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.defaultUriMatch; + } + + async setDefaultUriMatch(value: UriMatchType, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.defaultUriMatch = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAddLoginNotification(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAddLoginNotification ?? false + ); + } + + async setDisableAddLoginNotification(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAddLoginNotification = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAutoBiometricsPrompt(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAutoBiometricsPrompt ?? false + ); + } + + async setDisableAutoBiometricsPrompt(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAutoBiometricsPrompt = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableAutoTotpCopy(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableAutoTotpCopy ?? false + ); + } + + async setDisableAutoTotpCopy(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableAutoTotpCopy = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableBadgeCounter(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableBadgeCounter ?? false + ); + } + + async setDisableBadgeCounter(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableBadgeCounter = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableChangedPasswordNotification(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableChangedPasswordNotification ?? false + ); + } + + async setDisableChangedPasswordNotification( + value: boolean, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableChangedPasswordNotification = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableContextMenuItem(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableContextMenuItem ?? false + ); + } + + async setDisableContextMenuItem(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableContextMenuItem = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDisableFavicon(options?: StorageOptions): Promise { + return ( + ( + await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.disableFavicon ?? false + ); + } + + async setDisableFavicon(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.disableFavicon = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getDisableGa(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.disableGa ?? false + ); + } + + async setDisableGa(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.disableGa = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDontShowCardsCurrentTab(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.dontShowCardsCurrentTab ?? false + ); + } + + async setDontShowCardsCurrentTab(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.dontShowCardsCurrentTab = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getDontShowIdentitiesCurrentTab(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.dontShowIdentitiesCurrentTab ?? false + ); + } + + async setDontShowIdentitiesCurrentTab(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.dontShowIdentitiesCurrentTab = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEmail(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.profile?.email; + } + + async setEmail(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.email = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getEmailVerified(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.profile.emailVerified ?? false + ); + } + + async setEmailVerified(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.emailVerified = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableAlwaysOnTop(options?: StorageOptions): Promise { + const accountPreference = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.enableAlwaysOnTop; + const globalPreference = ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.enableAlwaysOnTop; + return accountPreference ?? globalPreference ?? false; + } + + async setEnableAlwaysOnTop(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.enableAlwaysOnTop = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableAlwaysOnTop = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableAutoFillOnPageLoad(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.enableAutoFillOnPageLoad ?? false + ); + } + + async setEnableAutoFillOnPageLoad(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.enableAutoFillOnPageLoad = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBiometric(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBiometrics ?? false + ); + } + + async setEnableBiometric(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBiometrics = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBrowserIntegration(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBrowserIntegration ?? false + ); + } + + async setEnableBrowserIntegration(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBrowserIntegration = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableBrowserIntegrationFingerprint(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableBrowserIntegrationFingerprint ?? false + ); + } + + async setEnableBrowserIntegrationFingerprint( + value: boolean, + options?: StorageOptions + ): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableBrowserIntegrationFingerprint = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableCloseToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableCloseToTray ?? false + ); + } + + async setEnableCloseToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableCloseToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableFullWidth(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.enableFullWidth ?? false + ); + } + + async setEnableFullWidth(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.enableFullWidth = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnableGravitars(options?: StorageOptions): Promise { + return ( + ( + await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ) + )?.settings?.enableGravitars ?? false + ); + } + + async setEnableGravitars(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.enableGravitars = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnableMinimizeToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableMinimizeToTray ?? false + ); + } + + async setEnableMinimizeToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableMinimizeToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableStartToTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableStartToTray ?? false + ); + } + + async setEnableStartToTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableStartToTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEnableTray(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.enableTray ?? false + ); + } + + async setEnableTray(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.enableTray = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.ciphers?.encrypted; + } + + async setEncryptedCiphers( + value: { [id: string]: CipherData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.ciphers.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedCollections( + options?: StorageOptions + ): Promise<{ [id: string]: CollectionData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.collections?.encrypted; + } + + async setEncryptedCollections( + value: { [id: string]: CollectionData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.collections.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedCryptoSymmetricKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys.cryptoSymmetricKey.encrypted; + } + + async setEncryptedCryptoSymmetricKey(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.cryptoSymmetricKey.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedFolders(options?: StorageOptions): Promise<{ [id: string]: FolderData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.folders?.encrypted; + } + + async setEncryptedFolders( + value: { [id: string]: FolderData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.folders.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEncryptedOrganizationKeys(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.organizationKeys.encrypted; + } + + async setEncryptedOrganizationKeys( + value: Map, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.organizationKeys.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPasswordGenerationHistory( + options?: StorageOptions + ): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.passwordGenerationHistory?.encrypted; + } + + async setEncryptedPasswordGenerationHistory( + value: GeneratedPasswordHistory[], + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.passwordGenerationHistory.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPinProtected(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.pinProtected?.encrypted; + } + + async setEncryptedPinProtected(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.pinProtected.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPolicies(options?: StorageOptions): Promise<{ [id: string]: PolicyData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.policies?.encrypted; + } + + async setEncryptedPolicies( + value: { [id: string]: PolicyData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.policies.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedPrivateKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.privateKey?.encrypted; + } + + async setEncryptedPrivateKey(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.privateKey.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedProviderKeys(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.providerKeys?.encrypted; + } + + async setEncryptedProviderKeys(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.providerKeys.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEncryptedSends(options?: StorageOptions): Promise<{ [id: string]: SendData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.data?.sends.encrypted; + } + + async setEncryptedSends( + value: { [id: string]: SendData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.data.sends.encrypted = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getEntityId(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.profile?.entityId; + } + + async setEntityId(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.profile.entityId = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEntityType(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.profile?.entityType; + } + + async setEntityType(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.profile.entityType = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getEnvironmentUrls(options?: StorageOptions): Promise { + if (this.state.activeUserId == null) { + return await this.getGlobalEnvironmentUrls(options); + } + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + return (await this.getAccount(options))?.settings?.environmentUrls ?? new EnvironmentUrls(); + } + + async setEnvironmentUrls(value: EnvironmentUrls, options?: StorageOptions): Promise { + // Global values are set on each change and the current global settings are passed to any newly authed accounts. + // This is to allow setting environement values before an account is active, while still allowing individual accounts to have their own environments. + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.environmentUrls = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEquivalentDomains(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.equivalentDomains; + } + + async setEquivalentDomains(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.equivalentDomains = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEventCollection(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.eventCollection; + } + + async setEventCollection(value: EventData[], options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.eventCollection = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getEverBeenUnlocked(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.profile + ?.everBeenUnlocked ?? false + ); + } + + async setEverBeenUnlocked(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.everBeenUnlocked = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getForcePasswordReset(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.profile + ?.forcePasswordReset ?? false + ); + } + + async setForcePasswordReset(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.profile.forcePasswordReset = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getInstalledVersion(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.installedVersion; + } + + async setInstalledVersion(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.installedVersion = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getIsAuthenticated(options?: StorageOptions): Promise { + return (await this.getAccessToken(options)) != null && (await this.getUserId(options)) != null; + } + + async getKdfIterations(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.kdfIterations; + } + + async setKdfIterations(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.kdfIterations = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getKdfType(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.kdfType; + } + + async setKdfType(value: KdfType, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.kdfType = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getKeyHash(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.keyHash; + } + + async setKeyHash(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.keyHash = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getLastActive(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + + const accountActivity = await this.storageService.get<{ [userId: string]: number }>( + keys.accountActivity, + options + ); + + if (accountActivity == null || Object.keys(accountActivity).length < 1) { + return null; + } + + return accountActivity[options.userId]; + } + + async setLastActive(value: number, options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + if (options.userId == null) { + return; + } + const accountActivity = + (await this.storageService.get<{ [userId: string]: number }>( + keys.accountActivity, + options + )) ?? {}; + accountActivity[options.userId] = value; + await this.storageService.save(keys.accountActivity, accountActivity, options); + } + + async getLastSync(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.profile?.lastSync; + } + + async setLastSync(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.profile.lastSync = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getLegacyEtmKey(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.keys?.legacyEtmKey; + } + + async setLegacyEtmKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.keys.legacyEtmKey = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getLocalData(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.data?.localData; + } + async setLocalData(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.data.localData = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getLocale(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.locale; + } + + async setLocale(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.locale = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getLoginRedirect(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.loginRedirect; + } + + async setLoginRedirect(value: any, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.loginRedirect = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getMainWindowSize(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.mainWindowSize; + } + + async setMainWindowSize(value: number, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.mainWindowSize = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getMinimizeOnCopyToClipboard(options?: StorageOptions): Promise { + return ( + (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.settings?.minimizeOnCopyToClipboard ?? false + ); + } + + async setMinimizeOnCopyToClipboard(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.minimizeOnCopyToClipboard = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNeverDomains(options?: StorageOptions): Promise<{ [id: string]: any }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.neverDomains; + } + + async setNeverDomains(value: { [id: string]: any }, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.neverDomains = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNoAutoPromptBiometrics(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.noAutoPromptBiometrics ?? false + ); + } + + async setNoAutoPromptBiometrics(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.noAutoPromptBiometrics = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getNoAutoPromptBiometricsText(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.noAutoPromptBiometricsText; + } + + async setNoAutoPromptBiometricsText(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.noAutoPromptBiometricsText = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getOpenAtLogin(options?: StorageOptions): Promise { + return ( + (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) + ?.openAtLogin ?? false + ); + } + + async setOpenAtLogin(value: boolean, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.openAtLogin = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getOrganizationInvitation(options?: StorageOptions): Promise { + return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.organizationInvitation; + } + + async setOrganizationInvitation(value: any, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + globals.organizationInvitation = value; + await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getOrganizations(options?: StorageOptions): Promise<{ [id: string]: OrganizationData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.organizations; + } + + async setOrganizations( + value: { [id: string]: OrganizationData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.organizations = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getPasswordGenerationOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.passwordGenerationOptions; + } + + async setPasswordGenerationOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.passwordGenerationOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getUsernameGenerationOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.usernameGenerationOptions; + } + + async setUsernameGenerationOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.usernameGenerationOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getGeneratorOptions(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.generatorOptions; + } + + async setGeneratorOptions(value: any, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.generatorOptions = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getProtectedPin(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.settings?.protectedPin; + } + + async setProtectedPin(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.settings.protectedPin = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getProviders(options?: StorageOptions): Promise<{ [id: string]: ProviderData }> { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.data?.providers; + } + + async setProviders( + value: { [id: string]: ProviderData }, + options?: StorageOptions + ): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.data.providers = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getPublicKey(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.keys?.publicKey; + } + + async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.keys.publicKey = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getRefreshToken(options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + return (await this.getAccount(options))?.tokens?.refreshToken; + } + + async setRefreshToken(value: string, options?: StorageOptions): Promise { + options = await this.getTimeoutBasedStorageOptions(options); + const account = await this.getAccount(options); + account.tokens.refreshToken = value; + await this.saveAccount(account, options); + } + + async getRememberedEmail(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.rememberedEmail; + } + + async setRememberedEmail(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.rememberedEmail = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getSecurityStamp(options?: StorageOptions): Promise { + return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions))) + ?.tokens?.securityStamp; + } + + async setSecurityStamp(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, this.defaultInMemoryOptions) + ); + account.tokens.securityStamp = value; + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); + } + + async getSettings(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) + )?.settings?.settings; + } + + async setSettings(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + account.settings.settings = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()) + ); + } + + async getSsoCodeVerifier(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.ssoCodeVerifier; + } + + async setSsoCodeVerifier(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.ssoCodeVerifier = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getSsoOrgIdentifier(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.ssoOrganizationIdentifier; + } + + async setSsoOrganizationIdentifier(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.ssoOrganizationIdentifier = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getSsoState(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.ssoState; + } + + async setSsoState(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.ssoState = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getTheme(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.theme; + } + + async setTheme(value: ThemeType, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.theme = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getTwoFactorToken(options?: StorageOptions): Promise { + return ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.twoFactorToken; + } + + async setTwoFactorToken(value: string, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + globals.twoFactorToken = value; + await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getUserId(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.userId; + } + + async getUsesKeyConnector(options?: StorageOptions): Promise { + return ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) + )?.profile?.usesKeyConnector; + } + + async setUsesKeyConnector(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + account.profile.usesKeyConnector = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + async getVaultTimeout(options?: StorageOptions): Promise { + const accountVaultTimeout = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.vaultTimeout; + return accountVaultTimeout; + } + + async setVaultTimeout(value: number, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.vaultTimeout = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getVaultTimeoutAction(options?: StorageOptions): Promise { + const accountVaultTimeoutAction = ( + await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.settings?.vaultTimeoutAction; + const globalVaultTimeoutAction = ( + await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) + )?.vaultTimeoutAction; + return accountVaultTimeoutAction ?? globalVaultTimeoutAction; + } + + async setVaultTimeoutAction(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + account.settings.vaultTimeoutAction = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) + ); + } + + async getStateVersion(): Promise { + return (await this.getGlobals(await this.defaultOnDiskLocalOptions())).stateVersion ?? 1; + } + + async setStateVersion(value: number): Promise { + const globals = await this.getGlobals(await this.defaultOnDiskOptions()); + globals.stateVersion = value; + await this.saveGlobals(globals, await this.defaultOnDiskOptions()); + } + + async getWindow(): Promise { + const globals = await this.getGlobals(await this.defaultOnDiskOptions()); + return globals?.window != null && Object.keys(globals.window).length > 0 + ? globals.window + : new WindowState(); + } + + async setWindow(value: WindowState, options?: StorageOptions): Promise { + const globals = await this.getGlobals( + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + globals.window = value; + return await this.saveGlobals( + globals, + this.reconcileOptions(options, await this.defaultOnDiskOptions()) + ); + } + + protected async getGlobals(options: StorageOptions): Promise { + let globals: TGlobalState; + if (this.useMemory(options.storageLocation)) { + globals = this.getGlobalsFromMemory(); + } + + if (this.useDisk && globals == null) { + globals = await this.getGlobalsFromDisk(options); + } + + return globals ?? this.createGlobals(); + } + + protected async saveGlobals(globals: TGlobalState, options: StorageOptions) { + return this.useMemory(options.storageLocation) + ? this.saveGlobalsToMemory(globals) + : await this.saveGlobalsToDisk(globals, options); + } + + protected getGlobalsFromMemory(): TGlobalState { + return this.state.globals; + } + + protected async getGlobalsFromDisk(options: StorageOptions): Promise { + return await this.storageService.get(keys.global, options); + } + + protected saveGlobalsToMemory(globals: TGlobalState): void { + this.state.globals = globals; + } + + protected async saveGlobalsToDisk(globals: TGlobalState, options: StorageOptions): Promise { + if (options.useSecureStorage) { + await this.secureStorageService.save(keys.global, globals, options); + } else { + await this.storageService.save(keys.global, globals, options); + } + } + + protected async getAccount(options: StorageOptions): Promise { + try { + let account: TAccount; + if (this.useMemory(options.storageLocation)) { + account = this.getAccountFromMemory(options); + } + + if (this.useDisk(options.storageLocation) && account == null) { + account = await this.getAccountFromDisk(options); + } + + return account; + } catch (e) { + this.logService.error(e); + } + } + + protected getAccountFromMemory(options: StorageOptions): TAccount { + if (this.state.accounts == null) { + return null; + } + return this.state.accounts[this.getUserIdFromMemory(options)]; + } + + protected getUserIdFromMemory(options: StorageOptions): string { + return options?.userId != null + ? this.state.accounts[options.userId]?.profile?.userId + : this.state.activeUserId; + } + + protected async getAccountFromDisk(options: StorageOptions): Promise { + if (options?.userId == null && this.state.activeUserId == null) { + return null; + } + + if (this.useAccountCache) { + const cachedAccount = this.accountDiskCache.get(options.userId); + if (cachedAccount != null) { + return cachedAccount; + } + } + + const account = options?.useSecureStorage + ? (await this.secureStorageService.get(options.userId, options)) ?? + (await this.storageService.get( + options.userId, + this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local }) + )) + : await this.storageService.get(options.userId, options); + + if (this.useAccountCache) { + this.accountDiskCache.set(options.userId, account); + } + return account; + } + + protected useMemory(storageLocation: StorageLocation) { + return storageLocation === StorageLocation.Memory || storageLocation === StorageLocation.Both; + } + + protected useDisk(storageLocation: StorageLocation) { + return storageLocation === StorageLocation.Disk || storageLocation === StorageLocation.Both; + } + + protected async saveAccount( + account: TAccount, + options: StorageOptions = { + storageLocation: StorageLocation.Both, + useSecureStorage: false, + } + ) { + return this.useMemory(options.storageLocation) + ? await this.saveAccountToMemory(account) + : await this.saveAccountToDisk(account, options); + } + + protected async saveAccountToDisk(account: TAccount, options: StorageOptions): Promise { + const storageLocation = options.useSecureStorage + ? this.secureStorageService + : this.storageService; + + await storageLocation.save(`${options.userId}`, account, options); + + if (this.useAccountCache) { + this.accountDiskCache.delete(options.userId); + } + } + + protected async saveAccountToMemory(account: TAccount): Promise { + if (this.getAccountFromMemory({ userId: account.profile.userId }) !== null) { + this.state.accounts[account.profile.userId] = account; + } + await this.pushAccounts(); + } + + protected async scaffoldNewAccountStorage(account: TAccount): Promise { + // We don't want to manipulate the referenced in memory account + const deepClone = JSON.parse(JSON.stringify(account)); + await this.scaffoldNewAccountLocalStorage(deepClone); + await this.scaffoldNewAccountSessionStorage(deepClone); + await this.scaffoldNewAccountMemoryStorage(deepClone); + } + + // TODO: There is a tech debt item for splitting up these methods - only Web uses multiple storage locations in its storageService. + // For now these methods exist with some redundancy to facilitate this special web requirement. + protected async scaffoldNewAccountLocalStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskLocalOptions() + ) + ); + // EnvironmentUrls are set before authenticating and should override whatever is stored from any previous session + const environmentUrls = account.settings.environmentUrls; + if (storedAccount?.settings != null) { + account.settings = storedAccount.settings; + } else if (await this.storageService.has(keys.tempAccountSettings)) { + account.settings = await this.storageService.get(keys.tempAccountSettings); + await this.storageService.remove(keys.tempAccountSettings); + } + account.settings.environmentUrls = environmentUrls; + if (account.settings.vaultTimeoutAction === "logOut" && account.settings.vaultTimeout != null) { + account.tokens.accessToken = null; + account.tokens.refreshToken = null; + account.profile.apiKeyClientId = null; + account.keys.apiKeyClientSecret = null; + } + await this.saveAccount( + account, + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskLocalOptions() + ) + ); + } + + protected async scaffoldNewAccountMemoryStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskMemoryOptions() + ) + ); + if (storedAccount?.settings != null) { + storedAccount.settings.environmentUrls = account.settings.environmentUrls; + account.settings = storedAccount.settings; + } + await this.storageService.save( + account.profile.userId, + account, + await this.defaultOnDiskMemoryOptions() + ); + await this.saveAccount( + account, + this.reconcileOptions( + { userId: account.profile.userId }, + await this.defaultOnDiskMemoryOptions() + ) + ); + } + + protected async scaffoldNewAccountSessionStorage(account: TAccount): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: account.profile.userId }, await this.defaultOnDiskOptions()) + ); + if (storedAccount?.settings != null) { + storedAccount.settings.environmentUrls = account.settings.environmentUrls; + account.settings = storedAccount.settings; + } + await this.storageService.save( + account.profile.userId, + account, + await this.defaultOnDiskMemoryOptions() + ); + await this.saveAccount( + account, + this.reconcileOptions({ userId: account.profile.userId }, await this.defaultOnDiskOptions()) + ); + } + // + + protected async pushAccounts(): Promise { + await this.pruneInMemoryAccounts(); + if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) { + this.accounts.next(null); + return; + } + + this.accounts.next(this.state.accounts); + } + + protected reconcileOptions( + requestedOptions: StorageOptions, + defaultOptions: StorageOptions + ): StorageOptions { + if (requestedOptions == null) { + return defaultOptions; + } + requestedOptions.userId = requestedOptions?.userId ?? defaultOptions.userId; + requestedOptions.storageLocation = + requestedOptions?.storageLocation ?? defaultOptions.storageLocation; + requestedOptions.useSecureStorage = + requestedOptions?.useSecureStorage ?? defaultOptions.useSecureStorage; + requestedOptions.htmlStorageLocation = + requestedOptions?.htmlStorageLocation ?? defaultOptions.htmlStorageLocation; + requestedOptions.keySuffix = requestedOptions?.keySuffix ?? defaultOptions.keySuffix; + return requestedOptions; + } + + protected get defaultInMemoryOptions(): StorageOptions { + return { storageLocation: StorageLocation.Memory, userId: this.state.activeUserId }; + } + + protected async defaultOnDiskOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Session, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + useSecureStorage: false, + }; + } + + protected async defaultOnDiskLocalOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Local, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + useSecureStorage: false, + }; + } + + protected async defaultOnDiskMemoryOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + htmlStorageLocation: HtmlStorageLocation.Memory, + userId: this.state.activeUserId ?? (await this.getUserId()), + useSecureStorage: false, + }; + } + + protected async defaultSecureStorageOptions(): Promise { + return { + storageLocation: StorageLocation.Disk, + useSecureStorage: true, + userId: this.state.activeUserId ?? (await this.getActiveUserIdFromStorage()), + }; + } + + protected async getActiveUserIdFromStorage(): Promise { + return await this.storageService.get(keys.activeUserId); + } + + protected async removeAccountFromLocalStorage( + userId: string = this.state.activeUserId + ): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskLocalOptions()) + ); + await this.saveAccount( + this.resetAccount(storedAccount), + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskLocalOptions()) + ); + } + + protected async removeAccountFromSessionStorage( + userId: string = this.state.activeUserId + ): Promise { + const storedAccount = await this.getAccount( + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskOptions()) + ); + await this.saveAccount( + this.resetAccount(storedAccount), + this.reconcileOptions({ userId: userId }, await this.defaultOnDiskOptions()) + ); + } + + protected async removeAccountFromSecureStorage( + userId: string = this.state.activeUserId + ): Promise { + await this.setCryptoMasterKeyAuto(null, { userId: userId }); + await this.setCryptoMasterKeyBiometric(null, { userId: userId }); + await this.setCryptoMasterKeyB64(null, { userId: userId }); + } + + protected removeAccountFromMemory(userId: string = this.state.activeUserId): void { + delete this.state.accounts[userId]; + if (this.useAccountCache) { + this.accountDiskCache.delete(userId); + } + } + + protected async pruneInMemoryAccounts() { + // We preserve settings for logged out accounts, but we don't want to consider them when thinking about active account state + for (const userId in this.state.accounts) { + if (!(await this.getIsAuthenticated({ userId: userId }))) { + this.removeAccountFromMemory(userId); + } + } + } + + // settings persist even on reset, and are not effected by this method + protected resetAccount(account: TAccount) { + const persistentAccountInformation = { settings: account.settings }; + return Object.assign(this.createAccount(), persistentAccountInformation); + } + + protected async setAccountEnvironmentUrls(account: TAccount): Promise { + account.settings.environmentUrls = await this.getGlobalEnvironmentUrls(); + return account; + } + + protected async getGlobalEnvironmentUrls(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskOptions()); + return (await this.getGlobals(options)).environmentUrls ?? new EnvironmentUrls(); + } + + protected clearDecryptedDataForActiveUser() { + const userId = this.state.activeUserId; + if (userId == null || this.state?.accounts[userId]?.data == null) { + return; + } + this.state.accounts[userId].data = new AccountData(); + } + + protected createAccount(init: Partial = null): TAccount { + return this.stateFactory.createAccount(init); + } + + protected createGlobals(init: Partial = null): TGlobalState { + return this.stateFactory.createGlobal(init); + } + + protected async deAuthenticateAccount(userId: string) { + await this.setAccessToken(null, { userId: userId }); + await this.setLastActive(null, { userId: userId }); + this.state.authenticatedAccounts = this.state.authenticatedAccounts.filter( + (activeUserId) => activeUserId !== userId + ); + await this.storageService.save(keys.authenticatedAccounts, this.state.authenticatedAccounts); + } + + protected async removeAccountFromDisk(userId: string) { + await this.removeAccountFromSessionStorage(userId); + await this.removeAccountFromLocalStorage(userId); + await this.removeAccountFromSecureStorage(userId); + } + + protected async dynamicallySetActiveUser() { + if (this.state.accounts == null || Object.keys(this.state.accounts).length < 1) { + await this.setActiveUser(null); + return; + } + for (const userId in this.state.accounts) { + if (userId == null) { + continue; + } + if (await this.getIsAuthenticated({ userId: userId })) { + await this.setActiveUser(userId); + break; + } + await this.setActiveUser(null); + } + } + + private async getTimeoutBasedStorageOptions(options?: StorageOptions): Promise { + const timeoutAction = await this.getVaultTimeoutAction({ userId: options?.userId }); + const timeout = await this.getVaultTimeout({ userId: options?.userId }); + const defaultOptions = + timeoutAction === "logOut" && timeout != null + ? this.defaultInMemoryOptions + : await this.defaultOnDiskOptions(); + return this.reconcileOptions(options, defaultOptions); + } + + private async saveSecureStorageKey(key: string, value: string, options?: StorageOptions) { + return value == null + ? await this.secureStorageService.remove(`${options.userId}${key}`, options) + : await this.secureStorageService.save(`${options.userId}${key}`, value, options); + } +} diff --git a/jslib/common/src/services/stateMigration.service.ts b/jslib/common/src/services/stateMigration.service.ts new file mode 100644 index 00000000..2b299898 --- /dev/null +++ b/jslib/common/src/services/stateMigration.service.ts @@ -0,0 +1,513 @@ +import { StorageService } from "../abstractions/storage.service"; +import { HtmlStorageLocation } from "../enums/htmlStorageLocation"; +import { KdfType } from "../enums/kdfType"; +import { StateVersion } from "../enums/stateVersion"; +import { ThemeType } from "../enums/themeType"; +import { StateFactory } from "../factories/stateFactory"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { EventData } from "../models/data/eventData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { Account, AccountSettings } from "../models/domain/account"; +import { EnvironmentUrls } from "../models/domain/environmentUrls"; +import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory"; +import { GlobalState } from "../models/domain/globalState"; +import { StorageOptions } from "../models/domain/storageOptions"; + +import { TokenService } from "./token.service"; + +// Originally (before January 2022) storage was handled as a flat key/value pair store. +// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration. +const v1Keys: { [key: string]: string } = { + accessToken: "accessToken", + alwaysShowDock: "alwaysShowDock", + autoConfirmFingerprints: "autoConfirmFingerprints", + autoFillOnPageLoadDefault: "autoFillOnPageLoadDefault", + biometricAwaitingAcceptance: "biometricAwaitingAcceptance", + biometricFingerprintValidated: "biometricFingerprintValidated", + biometricText: "biometricText", + biometricUnlock: "biometric", + clearClipboard: "clearClipboardKey", + clientId: "apikey_clientId", + clientSecret: "apikey_clientSecret", + collapsedGroupings: "collapsedGroupings", + convertAccountToKeyConnector: "convertAccountToKeyConnector", + defaultUriMatch: "defaultUriMatch", + disableAddLoginNotification: "disableAddLoginNotification", + disableAutoBiometricsPrompt: "noAutoPromptBiometrics", + disableAutoTotpCopy: "disableAutoTotpCopy", + disableBadgeCounter: "disableBadgeCounter", + disableChangedPasswordNotification: "disableChangedPasswordNotification", + disableContextMenuItem: "disableContextMenuItem", + disableFavicon: "disableFavicon", + disableGa: "disableGa", + dontShowCardsCurrentTab: "dontShowCardsCurrentTab", + dontShowIdentitiesCurrentTab: "dontShowIdentitiesCurrentTab", + emailVerified: "emailVerified", + enableAlwaysOnTop: "enableAlwaysOnTopKey", + enableAutoFillOnPageLoad: "enableAutoFillOnPageLoad", + enableBiometric: "enabledBiometric", + enableBrowserIntegration: "enableBrowserIntegration", + enableBrowserIntegrationFingerprint: "enableBrowserIntegrationFingerprint", + enableCloseToTray: "enableCloseToTray", + enableFullWidth: "enableFullWidth", + enableGravatars: "enableGravatars", + enableMinimizeToTray: "enableMinimizeToTray", + enableStartToTray: "enableStartToTrayKey", + enableTray: "enableTray", + encKey: "encKey", // Generated Symmetric Key + encOrgKeys: "encOrgKeys", + encPrivate: "encPrivateKey", + encProviderKeys: "encProviderKeys", + entityId: "entityId", + entityType: "entityType", + environmentUrls: "environmentUrls", + equivalentDomains: "equivalentDomains", + eventCollection: "eventCollection", + forcePasswordReset: "forcePasswordReset", + history: "generatedPasswordHistory", + installedVersion: "installedVersion", + kdf: "kdf", + kdfIterations: "kdfIterations", + key: "key", // Master Key + keyHash: "keyHash", + lastActive: "lastActive", + localData: "sitesLocalData", + locale: "locale", + mainWindowSize: "mainWindowSize", + minimizeOnCopyToClipboard: "minimizeOnCopyToClipboardKey", + neverDomains: "neverDomains", + noAutoPromptBiometricsText: "noAutoPromptBiometricsText", + openAtLogin: "openAtLogin", + passwordGenerationOptions: "passwordGenerationOptions", + pinProtected: "pinProtectedKey", + protectedPin: "protectedPin", + refreshToken: "refreshToken", + ssoCodeVerifier: "ssoCodeVerifier", + ssoIdentifier: "ssoOrgIdentifier", + ssoState: "ssoState", + stamp: "securityStamp", + theme: "theme", + userEmail: "userEmail", + userId: "userId", + usesConnector: "usesKeyConnector", + vaultTimeoutAction: "vaultTimeoutAction", + vaultTimeout: "lockOption", + rememberedEmail: "rememberedEmail", +}; + +const v1KeyPrefixes: { [key: string]: string } = { + ciphers: "ciphers_", + collections: "collections_", + folders: "folders_", + lastSync: "lastSync_", + policies: "policies_", + twoFactorToken: "twoFactorToken_", + organizations: "organizations_", + providers: "providers_", + sends: "sends_", + settings: "settings_", +}; + +const keys = { + global: "global", + authenticatedAccounts: "authenticatedAccounts", + activeUserId: "activeUserId", + tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication + accountActivity: "accountActivity", +}; + +const partialKeys = { + autoKey: "_masterkey_auto", + biometricKey: "_masterkey_biometric", + masterKey: "_masterkey", +}; + +export class StateMigrationService< + TGlobalState extends GlobalState = GlobalState, + TAccount extends Account = Account +> { + constructor( + protected storageService: StorageService, + protected secureStorageService: StorageService, + protected stateFactory: StateFactory + ) {} + + async needsMigration(): Promise { + const currentStateVersion = await this.getCurrentStateVersion(); + return currentStateVersion == null || currentStateVersion < StateVersion.Latest; + } + + async migrate(): Promise { + let currentStateVersion = await this.getCurrentStateVersion(); + while (currentStateVersion < StateVersion.Latest) { + switch (currentStateVersion) { + case StateVersion.One: + await this.migrateStateFrom1To2(); + break; + case StateVersion.Two: + await this.migrateStateFrom2To3(); + break; + case StateVersion.Three: + await this.migrateStateFrom3To4(); + break; + } + + currentStateVersion += 1; + } + } + + protected async migrateStateFrom1To2(): Promise { + const clearV1Keys = async (clearingUserId?: string) => { + for (const key in v1Keys) { + if (key == null) { + continue; + } + await this.set(v1Keys[key], null); + } + if (clearingUserId != null) { + for (const keyPrefix in v1KeyPrefixes) { + if (keyPrefix == null) { + continue; + } + await this.set(v1KeyPrefixes[keyPrefix] + userId, null); + } + } + }; + + // Some processes, like biometrics, may have already defined a value before migrations are run. + // We don't want to null out those values if they don't exist in the old storage scheme (like for new installs) + // So, the OOO for migration is that we: + // 1. Check for an existing storage value from the old storage structure OR + // 2. Check for a value already set by processes that run before migration OR + // 3. Assign the default value + const globals = + (await this.get(keys.global)) ?? this.stateFactory.createGlobal(null); + globals.stateVersion = StateVersion.Two; + globals.environmentUrls = + (await this.get(v1Keys.environmentUrls)) ?? globals.environmentUrls; + globals.locale = (await this.get(v1Keys.locale)) ?? globals.locale; + globals.noAutoPromptBiometrics = + (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? + globals.noAutoPromptBiometrics; + globals.noAutoPromptBiometricsText = + (await this.get(v1Keys.noAutoPromptBiometricsText)) ?? + globals.noAutoPromptBiometricsText; + globals.ssoCodeVerifier = + (await this.get(v1Keys.ssoCodeVerifier)) ?? globals.ssoCodeVerifier; + globals.ssoOrganizationIdentifier = + (await this.get(v1Keys.ssoIdentifier)) ?? globals.ssoOrganizationIdentifier; + globals.ssoState = (await this.get(v1Keys.ssoState)) ?? globals.ssoState; + globals.rememberedEmail = + (await this.get(v1Keys.rememberedEmail)) ?? globals.rememberedEmail; + globals.theme = (await this.get(v1Keys.theme)) ?? globals.theme; + globals.vaultTimeout = (await this.get(v1Keys.vaultTimeout)) ?? globals.vaultTimeout; + globals.vaultTimeoutAction = + (await this.get(v1Keys.vaultTimeoutAction)) ?? globals.vaultTimeoutAction; + globals.window = (await this.get(v1Keys.mainWindowSize)) ?? globals.window; + globals.enableTray = (await this.get(v1Keys.enableTray)) ?? globals.enableTray; + globals.enableMinimizeToTray = + (await this.get(v1Keys.enableMinimizeToTray)) ?? globals.enableMinimizeToTray; + globals.enableCloseToTray = + (await this.get(v1Keys.enableCloseToTray)) ?? globals.enableCloseToTray; + globals.enableStartToTray = + (await this.get(v1Keys.enableStartToTray)) ?? globals.enableStartToTray; + globals.openAtLogin = (await this.get(v1Keys.openAtLogin)) ?? globals.openAtLogin; + globals.alwaysShowDock = + (await this.get(v1Keys.alwaysShowDock)) ?? globals.alwaysShowDock; + globals.enableBrowserIntegration = + (await this.get(v1Keys.enableBrowserIntegration)) ?? + globals.enableBrowserIntegration; + globals.enableBrowserIntegrationFingerprint = + (await this.get(v1Keys.enableBrowserIntegrationFingerprint)) ?? + globals.enableBrowserIntegrationFingerprint; + + const userId = + (await this.get(v1Keys.userId)) ?? (await this.get(v1Keys.entityId)); + + const defaultAccount = this.stateFactory.createAccount(null); + const accountSettings: AccountSettings = { + autoConfirmFingerPrints: + (await this.get(v1Keys.autoConfirmFingerprints)) ?? + defaultAccount.settings.autoConfirmFingerPrints, + autoFillOnPageLoadDefault: + (await this.get(v1Keys.autoFillOnPageLoadDefault)) ?? + defaultAccount.settings.autoFillOnPageLoadDefault, + biometricLocked: null, + biometricUnlock: + (await this.get(v1Keys.biometricUnlock)) ?? + defaultAccount.settings.biometricUnlock, + clearClipboard: + (await this.get(v1Keys.clearClipboard)) ?? defaultAccount.settings.clearClipboard, + defaultUriMatch: + (await this.get(v1Keys.defaultUriMatch)) ?? defaultAccount.settings.defaultUriMatch, + disableAddLoginNotification: + (await this.get(v1Keys.disableAddLoginNotification)) ?? + defaultAccount.settings.disableAddLoginNotification, + disableAutoBiometricsPrompt: + (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? + defaultAccount.settings.disableAutoBiometricsPrompt, + disableAutoTotpCopy: + (await this.get(v1Keys.disableAutoTotpCopy)) ?? + defaultAccount.settings.disableAutoTotpCopy, + disableBadgeCounter: + (await this.get(v1Keys.disableBadgeCounter)) ?? + defaultAccount.settings.disableBadgeCounter, + disableChangedPasswordNotification: + (await this.get(v1Keys.disableChangedPasswordNotification)) ?? + defaultAccount.settings.disableChangedPasswordNotification, + disableContextMenuItem: + (await this.get(v1Keys.disableContextMenuItem)) ?? + defaultAccount.settings.disableContextMenuItem, + disableGa: (await this.get(v1Keys.disableGa)) ?? defaultAccount.settings.disableGa, + dontShowCardsCurrentTab: + (await this.get(v1Keys.dontShowCardsCurrentTab)) ?? + defaultAccount.settings.dontShowCardsCurrentTab, + dontShowIdentitiesCurrentTab: + (await this.get(v1Keys.dontShowIdentitiesCurrentTab)) ?? + defaultAccount.settings.dontShowIdentitiesCurrentTab, + enableAlwaysOnTop: + (await this.get(v1Keys.enableAlwaysOnTop)) ?? + defaultAccount.settings.enableAlwaysOnTop, + enableAutoFillOnPageLoad: + (await this.get(v1Keys.enableAutoFillOnPageLoad)) ?? + defaultAccount.settings.enableAutoFillOnPageLoad, + enableBiometric: + (await this.get(v1Keys.enableBiometric)) ?? + defaultAccount.settings.enableBiometric, + enableFullWidth: + (await this.get(v1Keys.enableFullWidth)) ?? + defaultAccount.settings.enableFullWidth, + enableGravitars: + (await this.get(v1Keys.enableGravatars)) ?? + defaultAccount.settings.enableGravitars, + environmentUrls: globals.environmentUrls ?? defaultAccount.settings.environmentUrls, + equivalentDomains: + (await this.get(v1Keys.equivalentDomains)) ?? + defaultAccount.settings.equivalentDomains, + minimizeOnCopyToClipboard: + (await this.get(v1Keys.minimizeOnCopyToClipboard)) ?? + defaultAccount.settings.minimizeOnCopyToClipboard, + neverDomains: + (await this.get(v1Keys.neverDomains)) ?? defaultAccount.settings.neverDomains, + passwordGenerationOptions: + (await this.get(v1Keys.passwordGenerationOptions)) ?? + defaultAccount.settings.passwordGenerationOptions, + pinProtected: { + decrypted: null, + encrypted: await this.get(v1Keys.pinProtected), + }, + protectedPin: await this.get(v1Keys.protectedPin), + settings: userId == null ? null : await this.get(v1KeyPrefixes.settings + userId), + vaultTimeout: + (await this.get(v1Keys.vaultTimeout)) ?? defaultAccount.settings.vaultTimeout, + vaultTimeoutAction: + (await this.get(v1Keys.vaultTimeoutAction)) ?? + defaultAccount.settings.vaultTimeoutAction, + }; + + // (userId == null) = no logged in user (so no known userId) and we need to temporarily store account specific settings in state to migrate on first auth + // (userId != null) = we have a currently authed user (so known userId) with encrypted data and other key settings we can move, no need to temporarily store account settings + if (userId == null) { + await this.set(keys.tempAccountSettings, accountSettings); + await this.set(keys.global, globals); + await this.set(keys.authenticatedAccounts, []); + await this.set(keys.activeUserId, null); + await clearV1Keys(); + return; + } + + globals.twoFactorToken = await this.get(v1KeyPrefixes.twoFactorToken + userId); + await this.set(keys.global, globals); + await this.set(userId, { + data: { + addEditCipherInfo: null, + ciphers: { + decrypted: null, + encrypted: await this.get<{ [id: string]: CipherData }>(v1KeyPrefixes.ciphers + userId), + }, + collapsedGroupings: null, + collections: { + decrypted: null, + encrypted: await this.get<{ [id: string]: CollectionData }>( + v1KeyPrefixes.collections + userId + ), + }, + eventCollection: await this.get(v1Keys.eventCollection), + folders: { + decrypted: null, + encrypted: await this.get<{ [id: string]: FolderData }>(v1KeyPrefixes.folders + userId), + }, + localData: null, + organizations: await this.get<{ [id: string]: OrganizationData }>( + v1KeyPrefixes.organizations + userId + ), + passwordGenerationHistory: { + decrypted: null, + encrypted: await this.get(v1Keys.history), + }, + policies: { + decrypted: null, + encrypted: await this.get<{ [id: string]: PolicyData }>(v1KeyPrefixes.policies + userId), + }, + providers: await this.get<{ [id: string]: ProviderData }>(v1KeyPrefixes.providers + userId), + sends: { + decrypted: null, + encrypted: await this.get<{ [id: string]: SendData }>(v1KeyPrefixes.sends + userId), + }, + }, + keys: { + apiKeyClientSecret: await this.get(v1Keys.clientSecret), + cryptoMasterKey: null, + cryptoMasterKeyAuto: null, + cryptoMasterKeyB64: null, + cryptoMasterKeyBiometric: null, + cryptoSymmetricKey: { + encrypted: await this.get(v1Keys.encKey), + decrypted: null, + }, + legacyEtmKey: null, + organizationKeys: { + decrypted: null, + encrypted: await this.get(v1Keys.encOrgKeys), + }, + privateKey: { + decrypted: null, + encrypted: await this.get(v1Keys.encPrivate), + }, + providerKeys: { + decrypted: null, + encrypted: await this.get(v1Keys.encProviderKeys), + }, + publicKey: null, + }, + profile: { + apiKeyClientId: await this.get(v1Keys.clientId), + authenticationStatus: null, + convertAccountToKeyConnector: await this.get(v1Keys.convertAccountToKeyConnector), + email: await this.get(v1Keys.userEmail), + emailVerified: await this.get(v1Keys.emailVerified), + entityId: null, + entityType: null, + everBeenUnlocked: null, + forcePasswordReset: null, + hasPremiumPersonally: null, + kdfIterations: await this.get(v1Keys.kdfIterations), + kdfType: await this.get(v1Keys.kdf), + keyHash: await this.get(v1Keys.keyHash), + lastSync: null, + userId: userId, + usesKeyConnector: null, + }, + settings: accountSettings, + tokens: { + accessToken: await this.get(v1Keys.accessToken), + decodedToken: null, + refreshToken: await this.get(v1Keys.refreshToken), + securityStamp: null, + }, + }); + + await this.set(keys.authenticatedAccounts, [userId]); + await this.set(keys.activeUserId, userId); + + const accountActivity: { [userId: string]: number } = { + [userId]: await this.get(v1Keys.lastActive), + }; + accountActivity[userId] = await this.get(v1Keys.lastActive); + await this.set(keys.accountActivity, accountActivity); + + await clearV1Keys(userId); + + if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "biometric" })) { + await this.secureStorageService.save( + `${userId}${partialKeys.biometricKey}`, + await this.secureStorageService.get(v1Keys.key, { keySuffix: "biometric" }), + { keySuffix: "biometric" } + ); + await this.secureStorageService.remove(v1Keys.key, { keySuffix: "biometric" }); + } + + if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "auto" })) { + await this.secureStorageService.save( + `${userId}${partialKeys.autoKey}`, + await this.secureStorageService.get(v1Keys.key, { keySuffix: "auto" }), + { keySuffix: "auto" } + ); + await this.secureStorageService.remove(v1Keys.key, { keySuffix: "auto" }); + } + + if (await this.secureStorageService.has(v1Keys.key)) { + await this.secureStorageService.save( + `${userId}${partialKeys.masterKey}`, + await this.secureStorageService.get(v1Keys.key) + ); + await this.secureStorageService.remove(v1Keys.key); + } + } + + protected async migrateStateFrom2To3(): Promise { + const authenticatedUserIds = await this.get(keys.authenticatedAccounts); + await Promise.all( + authenticatedUserIds.map(async (userId) => { + const account = await this.get(userId); + if ( + account?.profile?.hasPremiumPersonally === null && + account.tokens?.accessToken != null + ) { + const decodedToken = await TokenService.decodeToken(account.tokens.accessToken); + account.profile.hasPremiumPersonally = decodedToken.premium; + await this.set(userId, account); + } + }) + ); + + const globals = await this.getGlobals(); + globals.stateVersion = StateVersion.Three; + await this.set(keys.global, globals); + } + + protected async migrateStateFrom3To4(): Promise { + const authenticatedUserIds = await this.get(keys.authenticatedAccounts); + await Promise.all( + authenticatedUserIds.map(async (userId) => { + const account = await this.get(userId); + if (account?.profile?.everBeenUnlocked != null) { + delete account.profile.everBeenUnlocked; + return this.set(userId, account); + } + }) + ); + + const globals = await this.getGlobals(); + globals.stateVersion = StateVersion.Four; + await this.set(keys.global, globals); + } + + protected get options(): StorageOptions { + return { htmlStorageLocation: HtmlStorageLocation.Local }; + } + + protected get(key: string): Promise { + return this.storageService.get(key, this.options); + } + + protected set(key: string, value: any): Promise { + if (value == null) { + return this.storageService.remove(key, this.options); + } + return this.storageService.save(key, value, this.options); + } + + protected async getGlobals(): Promise { + return await this.get(keys.global); + } + + protected async getCurrentStateVersion(): Promise { + return (await this.getGlobals())?.stateVersion ?? StateVersion.One; + } +} diff --git a/jslib/common/src/services/sync.service.ts b/jslib/common/src/services/sync.service.ts new file mode 100644 index 00000000..93611238 --- /dev/null +++ b/jslib/common/src/services/sync.service.ts @@ -0,0 +1,400 @@ +import { ApiService } from "../abstractions/api.service"; +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { LogService } from "../abstractions/log.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { OrganizationService } from "../abstractions/organization.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { ProviderService } from "../abstractions/provider.service"; +import { SendService } from "../abstractions/send.service"; +import { SettingsService } from "../abstractions/settings.service"; +import { StateService } from "../abstractions/state.service"; +import { SyncService as SyncServiceAbstraction } from "../abstractions/sync.service"; +import { sequentialize } from "../misc/sequentialize"; +import { CipherData } from "../models/data/cipherData"; +import { CollectionData } from "../models/data/collectionData"; +import { FolderData } from "../models/data/folderData"; +import { OrganizationData } from "../models/data/organizationData"; +import { PolicyData } from "../models/data/policyData"; +import { ProviderData } from "../models/data/providerData"; +import { SendData } from "../models/data/sendData"; +import { CipherResponse } from "../models/response/cipherResponse"; +import { CollectionDetailsResponse } from "../models/response/collectionResponse"; +import { DomainsResponse } from "../models/response/domainsResponse"; +import { FolderResponse } from "../models/response/folderResponse"; +import { + SyncCipherNotification, + SyncFolderNotification, + SyncSendNotification, +} from "../models/response/notificationResponse"; +import { PolicyResponse } from "../models/response/policyResponse"; +import { ProfileResponse } from "../models/response/profileResponse"; +import { SendResponse } from "../models/response/sendResponse"; + +export class SyncService implements SyncServiceAbstraction { + syncInProgress = false; + + constructor( + private apiService: ApiService, + private settingsService: SettingsService, + private folderService: FolderService, + private cipherService: CipherService, + private cryptoService: CryptoService, + private collectionService: CollectionService, + private messagingService: MessagingService, + private policyService: PolicyService, + private sendService: SendService, + private logService: LogService, + private keyConnectorService: KeyConnectorService, + private stateService: StateService, + private organizationService: OrganizationService, + private providerService: ProviderService, + private logoutCallback: (expired: boolean) => Promise + ) {} + + async getLastSync(): Promise { + if ((await this.stateService.getUserId()) == null) { + return null; + } + + const lastSync = await this.stateService.getLastSync(); + if (lastSync) { + return new Date(lastSync); + } + + return null; + } + + async setLastSync(date: Date, userId?: string): Promise { + await this.stateService.setLastSync(date.toJSON(), { userId: userId }); + } + + @sequentialize(() => "fullSync") + async fullSync(forceSync: boolean, allowThrowOnError = false): Promise { + this.syncStarted(); + const isAuthenticated = await this.stateService.getIsAuthenticated(); + if (!isAuthenticated) { + return this.syncCompleted(false); + } + + const now = new Date(); + let needsSync = false; + try { + needsSync = await this.needsSyncing(forceSync); + } catch (e) { + if (allowThrowOnError) { + throw e; + } + } + + if (!needsSync) { + await this.setLastSync(now); + return this.syncCompleted(false); + } + + const userId = await this.stateService.getUserId(); + try { + await this.apiService.refreshIdentityToken(); + const response = await this.apiService.getSync(); + + await this.syncProfile(response.profile); + await this.syncFolders(userId, response.folders); + await this.syncCollections(response.collections); + await this.syncCiphers(userId, response.ciphers); + await this.syncSends(userId, response.sends); + await this.syncSettings(response.domains); + await this.syncPolicies(response.policies); + + await this.setLastSync(now); + return this.syncCompleted(true); + } catch (e) { + if (allowThrowOnError) { + throw e; + } else { + return this.syncCompleted(false); + } + } + } + + async syncUpsertFolder(notification: SyncFolderNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + const localFolder = await this.folderService.get(notification.id); + if ( + (!isEdit && localFolder == null) || + (isEdit && localFolder != null && localFolder.revisionDate < notification.revisionDate) + ) { + const remoteFolder = await this.apiService.getFolder(notification.id); + if (remoteFolder != null) { + const userId = await this.stateService.getUserId(); + await this.folderService.upsert(new FolderData(remoteFolder, userId)); + this.messagingService.send("syncedUpsertedFolder", { folderId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + this.logService.error(e); + } + } + return this.syncCompleted(false); + } + + async syncDeleteFolder(notification: SyncFolderNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.folderService.delete(notification.id); + this.messagingService.send("syncedDeletedFolder", { folderId: notification.id }); + this.syncCompleted(true); + return true; + } + return this.syncCompleted(false); + } + + async syncUpsertCipher(notification: SyncCipherNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + let shouldUpdate = true; + const localCipher = await this.cipherService.get(notification.id); + if (localCipher != null && localCipher.revisionDate >= notification.revisionDate) { + shouldUpdate = false; + } + + let checkCollections = false; + if (shouldUpdate) { + if (isEdit) { + shouldUpdate = localCipher != null; + checkCollections = true; + } else { + if (notification.collectionIds == null || notification.organizationId == null) { + shouldUpdate = localCipher == null; + } else { + shouldUpdate = false; + checkCollections = true; + } + } + } + + if ( + !shouldUpdate && + checkCollections && + notification.organizationId != null && + notification.collectionIds != null && + notification.collectionIds.length > 0 + ) { + const collections = await this.collectionService.getAll(); + if (collections != null) { + for (let i = 0; i < collections.length; i++) { + if (notification.collectionIds.indexOf(collections[i].id) > -1) { + shouldUpdate = true; + break; + } + } + } + } + + if (shouldUpdate) { + const remoteCipher = await this.apiService.getCipher(notification.id); + if (remoteCipher != null) { + const userId = await this.stateService.getUserId(); + await this.cipherService.upsert(new CipherData(remoteCipher, userId)); + this.messagingService.send("syncedUpsertedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + if (e != null && e.statusCode === 404 && isEdit) { + await this.cipherService.delete(notification.id); + this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + } + } + return this.syncCompleted(false); + } + + async syncDeleteCipher(notification: SyncCipherNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.cipherService.delete(notification.id); + this.messagingService.send("syncedDeletedCipher", { cipherId: notification.id }); + return this.syncCompleted(true); + } + return this.syncCompleted(false); + } + + async syncUpsertSend(notification: SyncSendNotification, isEdit: boolean): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + try { + const localSend = await this.sendService.get(notification.id); + if ( + (!isEdit && localSend == null) || + (isEdit && localSend != null && localSend.revisionDate < notification.revisionDate) + ) { + const remoteSend = await this.apiService.getSend(notification.id); + if (remoteSend != null) { + const userId = await this.stateService.getUserId(); + await this.sendService.upsert(new SendData(remoteSend, userId)); + this.messagingService.send("syncedUpsertedSend", { sendId: notification.id }); + return this.syncCompleted(true); + } + } + } catch (e) { + this.logService.error(e); + } + } + return this.syncCompleted(false); + } + + async syncDeleteSend(notification: SyncSendNotification): Promise { + this.syncStarted(); + if (await this.stateService.getIsAuthenticated()) { + await this.sendService.delete(notification.id); + this.messagingService.send("syncedDeletedSend", { sendId: notification.id }); + this.syncCompleted(true); + return true; + } + return this.syncCompleted(false); + } + + // Helpers + + private syncStarted() { + this.syncInProgress = true; + this.messagingService.send("syncStarted"); + } + + private syncCompleted(successfully: boolean): boolean { + this.syncInProgress = false; + this.messagingService.send("syncCompleted", { successfully: successfully }); + return successfully; + } + + private async needsSyncing(forceSync: boolean) { + if (forceSync) { + return true; + } + + const lastSync = await this.getLastSync(); + if (lastSync == null || lastSync.getTime() === 0) { + return true; + } + + const response = await this.apiService.getAccountRevisionDate(); + if (new Date(response) <= lastSync) { + return false; + } + return true; + } + + private async syncProfile(response: ProfileResponse) { + const stamp = await this.stateService.getSecurityStamp(); + if (stamp != null && stamp !== response.securityStamp) { + if (this.logoutCallback != null) { + await this.logoutCallback(true); + } + + throw new Error("Stamp has changed"); + } + + await this.cryptoService.setEncKey(response.key); + await this.cryptoService.setEncPrivateKey(response.privateKey); + await this.cryptoService.setProviderKeys(response.providers); + await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations); + await this.stateService.setSecurityStamp(response.securityStamp); + await this.stateService.setEmailVerified(response.emailVerified); + await this.stateService.setForcePasswordReset(response.forcePasswordReset); + await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector); + + const organizations: { [id: string]: OrganizationData } = {}; + response.organizations.forEach((o) => { + organizations[o.id] = new OrganizationData(o); + }); + + const providers: { [id: string]: ProviderData } = {}; + response.providers.forEach((p) => { + providers[p.id] = new ProviderData(p); + }); + + response.providerOrganizations.forEach((o) => { + if (organizations[o.id] == null) { + organizations[o.id] = new OrganizationData(o); + organizations[o.id].isProviderUser = true; + } + }); + + await this.organizationService.save(organizations); + await this.providerService.save(providers); + + if (await this.keyConnectorService.userNeedsMigration()) { + await this.keyConnectorService.setConvertAccountRequired(true); + this.messagingService.send("convertAccountToKeyConnector"); + } else { + this.keyConnectorService.removeConvertAccountRequired(); + } + } + + private async syncFolders(userId: string, response: FolderResponse[]) { + const folders: { [id: string]: FolderData } = {}; + response.forEach((f) => { + folders[f.id] = new FolderData(f, userId); + }); + return await this.folderService.replace(folders); + } + + private async syncCollections(response: CollectionDetailsResponse[]) { + const collections: { [id: string]: CollectionData } = {}; + response.forEach((c) => { + collections[c.id] = new CollectionData(c); + }); + return await this.collectionService.replace(collections); + } + + private async syncCiphers(userId: string, response: CipherResponse[]) { + const ciphers: { [id: string]: CipherData } = {}; + response.forEach((c) => { + ciphers[c.id] = new CipherData(c, userId); + }); + return await this.cipherService.replace(ciphers); + } + + private async syncSends(userId: string, response: SendResponse[]) { + const sends: { [id: string]: SendData } = {}; + response.forEach((s) => { + sends[s.id] = new SendData(s, userId); + }); + return await this.sendService.replace(sends); + } + + private async syncSettings(response: DomainsResponse) { + let eqDomains: string[][] = []; + if (response != null && response.equivalentDomains != null) { + eqDomains = eqDomains.concat(response.equivalentDomains); + } + + if (response != null && response.globalEquivalentDomains != null) { + response.globalEquivalentDomains.forEach((global) => { + if (global.domains.length > 0) { + eqDomains.push(global.domains); + } + }); + } + + return this.settingsService.setEquivalentDomains(eqDomains); + } + + private async syncPolicies(response: PolicyResponse[]) { + const policies: { [id: string]: PolicyData } = {}; + if (response != null) { + response.forEach((p) => { + policies[p.id] = new PolicyData(p); + }); + } + return await this.policyService.replace(policies); + } +} diff --git a/jslib/common/src/services/system.service.ts b/jslib/common/src/services/system.service.ts new file mode 100644 index 00000000..949b8ca4 --- /dev/null +++ b/jslib/common/src/services/system.service.ts @@ -0,0 +1,90 @@ +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service"; +import { Utils } from "../misc/utils"; + +export class SystemService implements SystemServiceAbstraction { + private reloadInterval: any = null; + private clearClipboardTimeout: any = null; + private clearClipboardTimeoutFunction: () => Promise = null; + + constructor( + private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, + private reloadCallback: () => Promise = null, + private stateService: StateService + ) {} + + async startProcessReload(): Promise { + if ( + (await this.stateService.getDecryptedPinProtected()) != null || + (await this.stateService.getBiometricLocked()) || + this.reloadInterval != null + ) { + return; + } + this.cancelProcessReload(); + this.reloadInterval = setInterval(async () => { + let doRefresh = false; + const lastActive = await this.stateService.getLastActive(); + if (lastActive != null) { + const diffSeconds = new Date().getTime() - lastActive; + // Don't refresh if they are still active in the window + doRefresh = diffSeconds >= 5000; + } + const biometricLockedFingerprintValidated = + (await this.stateService.getBiometricFingerprintValidated()) && + (await this.stateService.getBiometricLocked()); + if (doRefresh && !biometricLockedFingerprintValidated) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + this.messagingService.send("reloadProcess"); + if (this.reloadCallback != null) { + await this.reloadCallback(); + } + } + }, 10000); + } + + cancelProcessReload(): void { + if (this.reloadInterval != null) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + } + } + + async clearClipboard(clipboardValue: string, timeoutMs: number = null): Promise { + if (this.clearClipboardTimeout != null) { + clearTimeout(this.clearClipboardTimeout); + this.clearClipboardTimeout = null; + } + if (Utils.isNullOrWhitespace(clipboardValue)) { + return; + } + await this.stateService.getClearClipboard().then((clearSeconds) => { + if (clearSeconds == null) { + return; + } + if (timeoutMs == null) { + timeoutMs = clearSeconds * 1000; + } + this.clearClipboardTimeoutFunction = async () => { + const clipboardValueNow = await this.platformUtilsService.readFromClipboard(); + if (clipboardValue === clipboardValueNow) { + this.platformUtilsService.copyToClipboard("", { clearing: true }); + } + }; + this.clearClipboardTimeout = setTimeout(async () => { + await this.clearPendingClipboard(); + }, timeoutMs); + }); + } + + async clearPendingClipboard() { + if (this.clearClipboardTimeoutFunction != null) { + await this.clearClipboardTimeoutFunction(); + this.clearClipboardTimeoutFunction = null; + } + } +} diff --git a/jslib/common/src/services/token.service.ts b/jslib/common/src/services/token.service.ts new file mode 100644 index 00000000..a47cca7c --- /dev/null +++ b/jslib/common/src/services/token.service.ts @@ -0,0 +1,195 @@ +import { StateService } from "../abstractions/state.service"; +import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service"; +import { Utils } from "../misc/utils"; +import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; + +export class TokenService implements TokenServiceAbstraction { + static decodeToken(token: string): Promise { + if (token == null) { + throw new Error("Token not provided."); + } + + const parts = token.split("."); + if (parts.length !== 3) { + throw new Error("JWT must have 3 parts"); + } + + const decoded = Utils.fromUrlB64ToUtf8(parts[1]); + if (decoded == null) { + throw new Error("Cannot decode the token"); + } + + const decodedToken = JSON.parse(decoded); + return decodedToken; + } + + constructor(private stateService: StateService) {} + + async setTokens( + accessToken: string, + refreshToken: string, + clientIdClientSecret: [string, string] + ): Promise { + await this.setToken(accessToken); + await this.setRefreshToken(refreshToken); + if (clientIdClientSecret != null) { + await this.setClientId(clientIdClientSecret[0]); + await this.setClientSecret(clientIdClientSecret[1]); + } + } + + async setClientId(clientId: string): Promise { + return await this.stateService.setApiKeyClientId(clientId); + } + + async getClientId(): Promise { + return await this.stateService.getApiKeyClientId(); + } + + async setClientSecret(clientSecret: string): Promise { + return await this.stateService.setApiKeyClientSecret(clientSecret); + } + + async getClientSecret(): Promise { + return await this.stateService.getApiKeyClientSecret(); + } + + async setToken(token: string): Promise { + await this.stateService.setAccessToken(token); + } + + async getToken(): Promise { + return await this.stateService.getAccessToken(); + } + + async setRefreshToken(refreshToken: string): Promise { + return await this.stateService.setRefreshToken(refreshToken); + } + + async getRefreshToken(): Promise { + return await this.stateService.getRefreshToken(); + } + + async setTwoFactorToken(tokenResponse: IdentityTokenResponse): Promise { + return await this.stateService.setTwoFactorToken(tokenResponse.twoFactorToken); + } + + async getTwoFactorToken(): Promise { + return await this.stateService.getTwoFactorToken(); + } + + async clearTwoFactorToken(): Promise { + return await this.stateService.setTwoFactorToken(null); + } + + async clearToken(userId?: string): Promise { + await this.stateService.setAccessToken(null, { userId: userId }); + await this.stateService.setRefreshToken(null, { userId: userId }); + await this.stateService.setApiKeyClientId(null, { userId: userId }); + await this.stateService.setApiKeyClientSecret(null, { userId: userId }); + } + + // jwthelper methods + // ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js + + async decodeToken(token?: string): Promise { + const storedToken = await this.stateService.getDecodedToken(); + if (token === null && storedToken != null) { + return storedToken; + } + + token = token ?? (await this.stateService.getAccessToken()); + + if (token == null) { + throw new Error("Token not found."); + } + + return TokenService.decodeToken(token); + } + + async getTokenExpirationDate(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.exp === "undefined") { + return null; + } + + const d = new Date(0); // The 0 here is the key, which sets the date to the epoch + d.setUTCSeconds(decoded.exp); + return d; + } + + async tokenSecondsRemaining(offsetSeconds = 0): Promise { + const d = await this.getTokenExpirationDate(); + if (d == null) { + return 0; + } + + const msRemaining = d.valueOf() - (new Date().valueOf() + offsetSeconds * 1000); + return Math.round(msRemaining / 1000); + } + + async tokenNeedsRefresh(minutes = 5): Promise { + const sRemaining = await this.tokenSecondsRemaining(); + return sRemaining < 60 * minutes; + } + + async getUserId(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.sub === "undefined") { + throw new Error("No user id found"); + } + + return decoded.sub as string; + } + + async getEmail(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.email === "undefined") { + throw new Error("No email found"); + } + + return decoded.email as string; + } + + async getEmailVerified(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.email_verified === "undefined") { + throw new Error("No email verification found"); + } + + return decoded.email_verified as boolean; + } + + async getName(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.name === "undefined") { + return null; + } + + return decoded.name as string; + } + + async getPremium(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.premium === "undefined") { + return false; + } + + return decoded.premium as boolean; + } + + async getIssuer(): Promise { + const decoded = await this.decodeToken(); + if (typeof decoded.iss === "undefined") { + throw new Error("No issuer found"); + } + + return decoded.iss as string; + } + + async getIsExternal(): Promise { + const decoded = await this.decodeToken(); + + return Array.isArray(decoded.amr) && decoded.amr.includes("external"); + } +} diff --git a/jslib/common/src/services/totp.service.ts b/jslib/common/src/services/totp.service.ts new file mode 100644 index 00000000..730587c7 --- /dev/null +++ b/jslib/common/src/services/totp.service.ts @@ -0,0 +1,174 @@ +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { LogService } from "../abstractions/log.service"; +import { StateService } from "../abstractions/state.service"; +import { TotpService as TotpServiceAbstraction } from "../abstractions/totp.service"; +import { Utils } from "../misc/utils"; + +const B32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +const SteamChars = "23456789BCDFGHJKMNPQRTVWXY"; + +export class TotpService implements TotpServiceAbstraction { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private logService: LogService, + private stateService: StateService + ) {} + + async getCode(key: string): Promise { + if (key == null) { + return null; + } + let period = 30; + let alg: "sha1" | "sha256" | "sha512" = "sha1"; + let digits = 6; + let keyB32 = key; + const isOtpAuth = key.toLowerCase().indexOf("otpauth://") === 0; + const isSteamAuth = !isOtpAuth && key.toLowerCase().indexOf("steam://") === 0; + if (isOtpAuth) { + const params = Utils.getQueryParams(key); + if (params.has("digits") && params.get("digits") != null) { + try { + const digitParams = parseInt(params.get("digits").trim(), null); + if (digitParams > 10) { + digits = 10; + } else if (digitParams > 0) { + digits = digitParams; + } + } catch { + this.logService.error("Invalid digits param."); + } + } + if (params.has("period") && params.get("period") != null) { + try { + const periodParam = parseInt(params.get("period").trim(), null); + if (periodParam > 0) { + period = periodParam; + } + } catch { + this.logService.error("Invalid period param."); + } + } + if (params.has("secret") && params.get("secret") != null) { + keyB32 = params.get("secret"); + } + if (params.has("algorithm") && params.get("algorithm") != null) { + const algParam = params.get("algorithm").toLowerCase(); + if (algParam === "sha1" || algParam === "sha256" || algParam === "sha512") { + alg = algParam; + } + } + } else if (isSteamAuth) { + keyB32 = key.substr("steam://".length); + digits = 5; + } + + const epoch = Math.round(new Date().getTime() / 1000.0); + const timeHex = this.leftPad(this.decToHex(Math.floor(epoch / period)), 16, "0"); + const timeBytes = Utils.fromHexToArray(timeHex); + const keyBytes = this.b32ToBytes(keyB32); + + if (!keyBytes.length || !timeBytes.length) { + return null; + } + + const hash = await this.sign(keyBytes, timeBytes, alg); + if (hash.length === 0) { + return null; + } + + const offset = hash[hash.length - 1] & 0xf; + const binary = + ((hash[offset] & 0x7f) << 24) | + ((hash[offset + 1] & 0xff) << 16) | + ((hash[offset + 2] & 0xff) << 8) | + (hash[offset + 3] & 0xff); + + let otp = ""; + if (isSteamAuth) { + let fullCode = binary & 0x7fffffff; + for (let i = 0; i < digits; i++) { + otp += SteamChars[fullCode % SteamChars.length]; + fullCode = Math.trunc(fullCode / SteamChars.length); + } + } else { + otp = (binary % Math.pow(10, digits)).toString(); + otp = this.leftPad(otp, digits, "0"); + } + + return otp; + } + + getTimeInterval(key: string): number { + let period = 30; + if (key != null && key.toLowerCase().indexOf("otpauth://") === 0) { + const params = Utils.getQueryParams(key); + if (params.has("period") && params.get("period") != null) { + try { + period = parseInt(params.get("period").trim(), null); + } catch { + this.logService.error("Invalid period param."); + } + } + } + return period; + } + + async isAutoCopyEnabled(): Promise { + return !(await this.stateService.getDisableAutoTotpCopy()); + } + + // Helpers + + private leftPad(s: string, l: number, p: string): string { + if (l + 1 >= s.length) { + s = Array(l + 1 - s.length).join(p) + s; + } + return s; + } + + private decToHex(d: number): string { + return (d < 15.5 ? "0" : "") + Math.round(d).toString(16); + } + + private b32ToHex(s: string): string { + s = s.toUpperCase(); + let cleanedInput = ""; + + for (let i = 0; i < s.length; i++) { + if (B32Chars.indexOf(s[i]) < 0) { + continue; + } + + cleanedInput += s[i]; + } + s = cleanedInput; + + let bits = ""; + let hex = ""; + for (let i = 0; i < s.length; i++) { + const byteIndex = B32Chars.indexOf(s.charAt(i)); + if (byteIndex < 0) { + continue; + } + bits += this.leftPad(byteIndex.toString(2), 5, "0"); + } + for (let i = 0; i + 4 <= bits.length; i += 4) { + const chunk = bits.substr(i, 4); + hex = hex + parseInt(chunk, 2).toString(16); + } + return hex; + } + + private b32ToBytes(s: string): Uint8Array { + return Utils.fromHexToArray(this.b32ToHex(s)); + } + + private async sign( + keyBytes: Uint8Array, + timeBytes: Uint8Array, + alg: "sha1" | "sha256" | "sha512" + ) { + const signature = await this.cryptoFunctionService.hmac(timeBytes.buffer, keyBytes.buffer, alg); + return new Uint8Array(signature); + } +} diff --git a/jslib/common/src/services/twoFactor.service.ts b/jslib/common/src/services/twoFactor.service.ts new file mode 100644 index 00000000..7720d9be --- /dev/null +++ b/jslib/common/src/services/twoFactor.service.ts @@ -0,0 +1,186 @@ +import { I18nService } from "../abstractions/i18n.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { + TwoFactorProviderDetails, + TwoFactorService as TwoFactorServiceAbstraction, +} from "../abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "../enums/twoFactorProviderType"; +import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; + +export const TwoFactorProviders: Partial> = + { + [TwoFactorProviderType.Authenticator]: { + type: TwoFactorProviderType.Authenticator, + name: null as string, + description: null as string, + priority: 1, + sort: 1, + premium: false, + }, + [TwoFactorProviderType.Yubikey]: { + type: TwoFactorProviderType.Yubikey, + name: null as string, + description: null as string, + priority: 3, + sort: 2, + premium: true, + }, + [TwoFactorProviderType.Duo]: { + type: TwoFactorProviderType.Duo, + name: "Duo", + description: null as string, + priority: 2, + sort: 3, + premium: true, + }, + [TwoFactorProviderType.OrganizationDuo]: { + type: TwoFactorProviderType.OrganizationDuo, + name: "Duo (Organization)", + description: null as string, + priority: 10, + sort: 4, + premium: false, + }, + [TwoFactorProviderType.Email]: { + type: TwoFactorProviderType.Email, + name: null as string, + description: null as string, + priority: 0, + sort: 6, + premium: false, + }, + [TwoFactorProviderType.WebAuthn]: { + type: TwoFactorProviderType.WebAuthn, + name: null as string, + description: null as string, + priority: 4, + sort: 5, + premium: true, + }, + }; + +export class TwoFactorService implements TwoFactorServiceAbstraction { + private twoFactorProvidersData: Map; + private selectedTwoFactorProviderType: TwoFactorProviderType = null; + + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService + ) {} + + init() { + TwoFactorProviders[TwoFactorProviderType.Email].name = this.i18nService.t("emailTitle"); + TwoFactorProviders[TwoFactorProviderType.Email].description = this.i18nService.t("emailDesc"); + + TwoFactorProviders[TwoFactorProviderType.Authenticator].name = + this.i18nService.t("authenticatorAppTitle"); + TwoFactorProviders[TwoFactorProviderType.Authenticator].description = + this.i18nService.t("authenticatorAppDesc"); + + TwoFactorProviders[TwoFactorProviderType.Duo].description = this.i18nService.t("duoDesc"); + + TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].name = + "Duo (" + this.i18nService.t("organization") + ")"; + TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].description = + this.i18nService.t("duoOrganizationDesc"); + + TwoFactorProviders[TwoFactorProviderType.WebAuthn].name = this.i18nService.t("webAuthnTitle"); + TwoFactorProviders[TwoFactorProviderType.WebAuthn].description = + this.i18nService.t("webAuthnDesc"); + + TwoFactorProviders[TwoFactorProviderType.Yubikey].name = this.i18nService.t("yubiKeyTitle"); + TwoFactorProviders[TwoFactorProviderType.Yubikey].description = + this.i18nService.t("yubiKeyDesc"); + } + + getSupportedProviders(win: Window): TwoFactorProviderDetails[] { + const providers: any[] = []; + if (this.twoFactorProvidersData == null) { + return providers; + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.OrganizationDuo) && + this.platformUtilsService.supportsDuo() + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.OrganizationDuo]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Authenticator)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Authenticator]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Yubikey)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Yubikey]); + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.Duo) && + this.platformUtilsService.supportsDuo() + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Duo]); + } + + if ( + this.twoFactorProvidersData.has(TwoFactorProviderType.WebAuthn) && + this.platformUtilsService.supportsWebAuthn(win) + ) { + providers.push(TwoFactorProviders[TwoFactorProviderType.WebAuthn]); + } + + if (this.twoFactorProvidersData.has(TwoFactorProviderType.Email)) { + providers.push(TwoFactorProviders[TwoFactorProviderType.Email]); + } + + return providers; + } + + getDefaultProvider(webAuthnSupported: boolean): TwoFactorProviderType { + if (this.twoFactorProvidersData == null) { + return null; + } + + if ( + this.selectedTwoFactorProviderType != null && + this.twoFactorProvidersData.has(this.selectedTwoFactorProviderType) + ) { + return this.selectedTwoFactorProviderType; + } + + let providerType: TwoFactorProviderType = null; + let providerPriority = -1; + this.twoFactorProvidersData.forEach((_value, type) => { + const provider = (TwoFactorProviders as any)[type]; + if (provider != null && provider.priority > providerPriority) { + if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) { + return; + } + + providerType = type; + providerPriority = provider.priority; + } + }); + + return providerType; + } + + setSelectedProvider(type: TwoFactorProviderType) { + this.selectedTwoFactorProviderType = type; + } + + clearSelectedProvider() { + this.selectedTwoFactorProviderType = null; + } + + setProviders(response: IdentityTwoFactorResponse) { + this.twoFactorProvidersData = response.twoFactorProviders2; + } + + clearProviders() { + this.twoFactorProvidersData = null; + } + + getProviders() { + return this.twoFactorProvidersData; + } +} diff --git a/jslib/common/src/services/userVerification.service.ts b/jslib/common/src/services/userVerification.service.ts new file mode 100644 index 00000000..65207ac0 --- /dev/null +++ b/jslib/common/src/services/userVerification.service.ts @@ -0,0 +1,88 @@ +import { ApiService } from "../abstractions/api.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { I18nService } from "../abstractions/i18n.service"; +import { UserVerificationService as UserVerificationServiceAbstraction } from "../abstractions/userVerification.service"; +import { VerificationType } from "../enums/verificationType"; +import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest"; +import { SecretVerificationRequest } from "../models/request/secretVerificationRequest"; +import { Verification } from "../types/verification"; + +/** + * Used for general-purpose user verification throughout the app. + * Use it to verify the input collected by UserVerificationComponent. + */ +export class UserVerificationService implements UserVerificationServiceAbstraction { + constructor( + private cryptoService: CryptoService, + private i18nService: I18nService, + private apiService: ApiService + ) {} + + /** + * Create a new request model to be used for server-side verification + * @param verification User-supplied verification data (Master Password or OTP) + * @param requestClass The request model to create + * @param alreadyHashed Whether the master password is already hashed + */ + async buildRequest( + verification: Verification, + requestClass?: new () => T, + alreadyHashed?: boolean + ) { + this.validateInput(verification); + + const request = + requestClass != null ? new requestClass() : (new SecretVerificationRequest() as T); + + if (verification.type === VerificationType.OTP) { + request.otp = verification.secret; + } else { + request.masterPasswordHash = alreadyHashed + ? verification.secret + : await this.cryptoService.hashPassword(verification.secret, null); + } + + return request; + } + + /** + * Used to verify the Master Password client-side, or send the OTP to the server for verification (with no other data) + * Generally used for client-side verification only. + * @param verification User-supplied verification data (Master Password or OTP) + */ + async verifyUser(verification: Verification): Promise { + this.validateInput(verification); + + if (verification.type === VerificationType.OTP) { + const request = new VerifyOTPRequest(verification.secret); + try { + await this.apiService.postAccountVerifyOTP(request); + } catch (e) { + throw new Error(this.i18nService.t("invalidVerificationCode")); + } + } else { + const passwordValid = await this.cryptoService.compareAndUpdateKeyHash( + verification.secret, + null + ); + if (!passwordValid) { + throw new Error(this.i18nService.t("invalidMasterPassword")); + } + } + return true; + } + + async requestOTP() { + await this.apiService.postAccountRequestOTP(); + } + + private validateInput(verification: Verification) { + if (verification?.secret == null || verification.secret === "") { + if (verification.type === VerificationType.OTP) { + throw new Error(this.i18nService.t("verificationCodeRequired")); + } else { + throw new Error(this.i18nService.t("masterPassRequired")); + } + } + } +} diff --git a/jslib/common/src/services/usernameGeneration.service.ts b/jslib/common/src/services/usernameGeneration.service.ts new file mode 100644 index 00000000..e4201d6e --- /dev/null +++ b/jslib/common/src/services/usernameGeneration.service.ts @@ -0,0 +1,128 @@ +import { CryptoService } from "../abstractions/crypto.service"; +import { StateService } from "../abstractions/state.service"; +import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service"; +import { EEFLongWordList } from "../misc/wordlist"; + +const DefaultOptions = { + type: "word", + wordCapitalize: true, + wordIncludeNumber: true, + subaddressType: "random", + catchallType: "random", +}; + +export class UsernameGenerationService implements BaseUsernameGenerationService { + constructor(private cryptoService: CryptoService, private stateService: StateService) {} + + generateUsername(options: any): Promise { + if (options.type === "catchall") { + return this.generateCatchall(options); + } else if (options.type === "subaddress") { + return this.generateSubaddress(options); + } else if (options.type === "forwarded") { + return this.generateSubaddress(options); + } else { + return this.generateWord(options); + } + } + + async generateWord(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.wordCapitalize == null) { + o.wordCapitalize = true; + } + if (o.wordIncludeNumber == null) { + o.wordIncludeNumber = true; + } + + const wordIndex = await this.cryptoService.randomNumber(0, EEFLongWordList.length - 1); + let word = EEFLongWordList[wordIndex]; + if (o.wordCapitalize) { + word = word.charAt(0).toUpperCase() + word.slice(1); + } + if (o.wordIncludeNumber) { + const num = await this.cryptoService.randomNumber(1, 9999); + word = word + this.zeroPad(num.toString(), 4); + } + return word; + } + + async generateSubaddress(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + const subaddressEmail = o.subaddressEmail; + if (subaddressEmail == null || subaddressEmail.length < 3) { + return o.subaddressEmail; + } + const atIndex = subaddressEmail.indexOf("@"); + if (atIndex < 1 || atIndex >= subaddressEmail.length - 1) { + return subaddressEmail; + } + if (o.subaddressType == null) { + o.subaddressType = "random"; + } + + const emailBeginning = subaddressEmail.substr(0, atIndex); + const emailEnding = subaddressEmail.substr(atIndex + 1, subaddressEmail.length); + + let subaddressString = ""; + if (o.subaddressType === "random") { + subaddressString = await this.randomString(8); + } else if (o.subaddressType === "website-name") { + subaddressString = o.website; + } + return emailBeginning + "+" + subaddressString + "@" + emailEnding; + } + + async generateCatchall(options: any): Promise { + const o = Object.assign({}, DefaultOptions, options); + + if (o.catchallDomain == null || o.catchallDomain === "") { + return null; + } + if (o.catchallType == null) { + o.catchallType = "random"; + } + + let startString = ""; + if (o.catchallType === "random") { + startString = await this.randomString(8); + } else if (o.catchallType === "website-name") { + startString = o.website; + } + return startString + "@" + o.catchallDomain; + } + + async getOptions(): Promise { + let options = await this.stateService.getUsernameGenerationOptions(); + if (options == null) { + options = Object.assign({}, DefaultOptions); + } else { + options = Object.assign({}, DefaultOptions, options); + } + await this.stateService.setUsernameGenerationOptions(options); + return options; + } + + async saveOptions(options: any) { + await this.stateService.setUsernameGenerationOptions(options); + } + + private async randomString(length: number) { + let str = ""; + const charSet = "abcdefghijklmnopqrstuvwxyz1234567890"; + for (let i = 0; i < length; i++) { + const randomCharIndex = await this.cryptoService.randomNumber(0, charSet.length - 1); + str += charSet.charAt(randomCharIndex); + } + return str; + } + + // ref: https://stackoverflow.com/a/10073788 + private zeroPad(number: string, width: number) { + return number.length >= width + ? number + : new Array(width - number.length + 1).join("0") + number; + } +} diff --git a/jslib/common/src/services/vaultTimeout.service.ts b/jslib/common/src/services/vaultTimeout.service.ts new file mode 100644 index 00000000..a6513d65 --- /dev/null +++ b/jslib/common/src/services/vaultTimeout.service.ts @@ -0,0 +1,222 @@ +import { CipherService } from "../abstractions/cipher.service"; +import { CollectionService } from "../abstractions/collection.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { FolderService } from "../abstractions/folder.service"; +import { KeyConnectorService } from "../abstractions/keyConnector.service"; +import { MessagingService } from "../abstractions/messaging.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { PolicyService } from "../abstractions/policy.service"; +import { SearchService } from "../abstractions/search.service"; +import { StateService } from "../abstractions/state.service"; +import { TokenService } from "../abstractions/token.service"; +import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../abstractions/vaultTimeout.service"; +import { KeySuffixOptions } from "../enums/keySuffixOptions"; +import { PolicyType } from "../enums/policyType"; + +export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { + private inited = false; + + constructor( + private cipherService: CipherService, + private folderService: FolderService, + private collectionService: CollectionService, + private cryptoService: CryptoService, + protected platformUtilsService: PlatformUtilsService, + private messagingService: MessagingService, + private searchService: SearchService, + private tokenService: TokenService, + private policyService: PolicyService, + private keyConnectorService: KeyConnectorService, + private stateService: StateService, + private lockedCallback: (userId?: string) => Promise = null, + private loggedOutCallback: (userId?: string) => Promise = null + ) {} + + init(checkOnInterval: boolean) { + if (this.inited) { + return; + } + + this.inited = true; + if (checkOnInterval) { + this.startCheck(); + } + } + + startCheck() { + this.checkVaultTimeout(); + setInterval(() => this.checkVaultTimeout(), 10 * 1000); // check every 10 seconds + } + + // Keys aren't stored for a device that is locked or logged out. + async isLocked(userId?: string): Promise { + const neverLock = + (await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId)) && + !(await this.stateService.getEverBeenUnlocked({ userId: userId })); + if (neverLock) { + // TODO: This also _sets_ the key so when we check memory in the next line it finds a key. + // We should refactor here. + await this.cryptoService.getKey(KeySuffixOptions.Auto, userId); + } + + return !(await this.cryptoService.hasKeyInMemory(userId)); + } + + async checkVaultTimeout(): Promise { + if (await this.platformUtilsService.isViewOpen()) { + return; + } + + for (const userId in this.stateService.accounts.getValue()) { + if (userId != null && (await this.shouldLock(userId))) { + await this.executeTimeoutAction(userId); + } + } + } + + async lock(allowSoftLock = false, userId?: string): Promise { + const authed = await this.stateService.getIsAuthenticated({ userId: userId }); + if (!authed) { + return; + } + + if (await this.keyConnectorService.getUsesKeyConnector()) { + const pinSet = await this.isPinLockSet(); + const pinLock = + (pinSet[0] && (await this.stateService.getDecryptedPinProtected()) != null) || pinSet[1]; + + if (!pinLock && !(await this.isBiometricLockSet())) { + await this.logOut(userId); + } + } + + if (userId == null || userId === (await this.stateService.getUserId())) { + this.searchService.clearIndex(); + } + + await this.stateService.setEverBeenUnlocked(true, { userId: userId }); + await this.stateService.setBiometricLocked(true, { userId: userId }); + await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); + + await this.cryptoService.clearKey(false, userId); + await this.cryptoService.clearOrgKeys(true, userId); + await this.cryptoService.clearKeyPair(true, userId); + await this.cryptoService.clearEncKey(true, userId); + + await this.folderService.clearCache(userId); + await this.cipherService.clearCache(userId); + await this.collectionService.clearCache(userId); + + this.messagingService.send("locked", { userId: userId }); + + if (this.lockedCallback != null) { + await this.lockedCallback(userId); + } + } + + async logOut(userId?: string): Promise { + if (this.loggedOutCallback != null) { + await this.loggedOutCallback(userId); + } + } + + async setVaultTimeoutOptions(timeout: number, action: string): Promise { + await this.stateService.setVaultTimeout(timeout); + + // We swap these tokens from being on disk for lock actions, and in memory for logout actions + // Get them here to set them to their new location after changing the timeout action and clearing if needed + const token = await this.tokenService.getToken(); + const refreshToken = await this.tokenService.getRefreshToken(); + const clientId = await this.tokenService.getClientId(); + const clientSecret = await this.tokenService.getClientSecret(); + + const currentAction = await this.stateService.getVaultTimeoutAction(); + if ((timeout != null || timeout === 0) && action === "logOut" && action !== currentAction) { + // if we have a vault timeout and the action is log out, reset tokens + await this.tokenService.clearToken(); + } + + await this.stateService.setVaultTimeoutAction(action); + + await this.tokenService.setToken(token); + await this.tokenService.setRefreshToken(refreshToken); + await this.tokenService.setClientId(clientId); + await this.tokenService.setClientSecret(clientSecret); + + await this.cryptoService.toggleKey(); + } + + async isPinLockSet(): Promise<[boolean, boolean]> { + const protectedPin = await this.stateService.getProtectedPin(); + const pinProtectedKey = await this.stateService.getEncryptedPinProtected(); + return [protectedPin != null, pinProtectedKey != null]; + } + + async isBiometricLockSet(): Promise { + return await this.stateService.getBiometricUnlock(); + } + + async getVaultTimeout(userId?: string): Promise { + const vaultTimeout = await this.stateService.getVaultTimeout({ userId: userId }); + + if ( + await this.policyService.policyAppliesToUser(PolicyType.MaximumVaultTimeout, null, userId) + ) { + const policy = await this.policyService.getAll(PolicyType.MaximumVaultTimeout, userId); + // Remove negative values, and ensure it's smaller than maximum allowed value according to policy + let timeout = Math.min(vaultTimeout, policy[0].data.minutes); + + if (vaultTimeout == null || timeout < 0) { + timeout = policy[0].data.minutes; + } + + // We really shouldn't need to set the value here, but multiple services relies on this value being correct. + if (vaultTimeout !== timeout) { + await this.stateService.setVaultTimeout(timeout, { userId: userId }); + } + + return timeout; + } + + return vaultTimeout; + } + + async clear(userId?: string): Promise { + await this.stateService.setEverBeenUnlocked(false, { userId: userId }); + await this.stateService.setDecryptedPinProtected(null, { userId: userId }); + await this.stateService.setProtectedPin(null, { userId: userId }); + } + + private async isLoggedOut(userId?: string): Promise { + return !(await this.stateService.getIsAuthenticated({ userId: userId })); + } + + private async shouldLock(userId: string): Promise { + if (await this.isLoggedOut(userId)) { + return false; + } + + if (await this.isLocked(userId)) { + return false; + } + + const vaultTimeout = await this.getVaultTimeout(userId); + if (vaultTimeout == null || vaultTimeout < 0) { + return false; + } + + const lastActive = await this.stateService.getLastActive({ userId: userId }); + if (lastActive == null) { + return false; + } + + const vaultTimeoutSeconds = vaultTimeout * 60; + const diffSeconds = (new Date().getTime() - lastActive) / 1000; + return diffSeconds >= vaultTimeoutSeconds; + } + + private async executeTimeoutAction(userId: string): Promise { + const timeoutAction = await this.stateService.getVaultTimeoutAction({ userId: userId }); + timeoutAction === "logOut" ? await this.logOut(userId) : await this.lock(true, userId); + } +} diff --git a/jslib/common/src/services/webCryptoFunction.service.ts b/jslib/common/src/services/webCryptoFunction.service.ts new file mode 100644 index 00000000..b863f226 --- /dev/null +++ b/jslib/common/src/services/webCryptoFunction.service.ts @@ -0,0 +1,356 @@ +import * as forge from "node-forge"; + +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { Utils } from "../misc/utils"; +import { DecryptParameters } from "../models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; + +export class WebCryptoFunctionService implements CryptoFunctionService { + private crypto: Crypto; + private subtle: SubtleCrypto; + + constructor(win: Window) { + this.crypto = typeof win.crypto !== "undefined" ? win.crypto : null; + this.subtle = + !!this.crypto && typeof win.crypto.subtle !== "undefined" ? win.crypto.subtle : null; + } + + async pbkdf2( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ): Promise { + const wcLen = algorithm === "sha256" ? 256 : 512; + const passwordBuf = this.toBuf(password); + const saltBuf = this.toBuf(salt); + + const pbkdf2Params: Pbkdf2Params = { + name: "PBKDF2", + salt: saltBuf, + iterations: iterations, + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey( + "raw", + passwordBuf, + { name: "PBKDF2" } as any, + false, + ["deriveBits"] + ); + return await this.subtle.deriveBits(pbkdf2Params, impKey, wcLen); + } + + async hkdf( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const saltBuf = this.toBuf(salt); + const infoBuf = this.toBuf(info); + + const hkdfParams: HkdfParams = { + name: "HKDF", + salt: saltBuf, + info: infoBuf, + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey("raw", ikm, { name: "HKDF" } as any, false, [ + "deriveBits", + ]); + return await this.subtle.deriveBits(hkdfParams as any, impKey, outputByteSize * 8); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdfExpand( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const hashLen = algorithm === "sha256" ? 32 : 64; + if (outputByteSize > 255 * hashLen) { + throw new Error("outputByteSize is too large."); + } + const prkArr = new Uint8Array(prk); + if (prkArr.length < hashLen) { + throw new Error("prk is too small."); + } + const infoBuf = this.toBuf(info); + const infoArr = new Uint8Array(infoBuf); + let runningOkmLength = 0; + let previousT = new Uint8Array(0); + const n = Math.ceil(outputByteSize / hashLen); + const okm = new Uint8Array(n * hashLen); + for (let i = 0; i < n; i++) { + const t = new Uint8Array(previousT.length + infoArr.length + 1); + t.set(previousT); + t.set(infoArr, previousT.length); + t.set([i + 1], t.length - 1); + previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + okm.set(previousT, runningOkmLength); + runningOkmLength += previousT.length; + if (runningOkmLength >= outputByteSize) { + break; + } + } + return okm.slice(0, outputByteSize).buffer; + } + + async hash( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ): Promise { + if (algorithm === "md5") { + const md = algorithm === "md5" ? forge.md.md5.create() : forge.md.sha1.create(); + const valueBytes = this.toByteString(value); + md.update(valueBytes, "raw"); + return Utils.fromByteStringToArray(md.digest().data).buffer; + } + + const valueBuf = this.toBuf(value); + return await this.subtle.digest({ name: this.toWebCryptoAlgorithm(algorithm) }, valueBuf); + } + + async hmac( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + const signingAlgorithm = { + name: "HMAC", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + + const impKey = await this.subtle.importKey("raw", key, signingAlgorithm, false, ["sign"]); + return await this.subtle.sign(signingAlgorithm, impKey, value); + } + + // Safely compare two values in a way that protects against timing attacks (Double HMAC Verification). + // ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/ + // ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy + async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + const macKey = await this.randomBytes(32); + const signingAlgorithm = { + name: "HMAC", + hash: { name: "SHA-256" }, + }; + const impKey = await this.subtle.importKey("raw", macKey, signingAlgorithm, false, ["sign"]); + const mac1 = await this.subtle.sign(signingAlgorithm, impKey, a); + const mac2 = await this.subtle.sign(signingAlgorithm, impKey, b); + + if (mac1.byteLength !== mac2.byteLength) { + return false; + } + + const arr1 = new Uint8Array(mac1); + const arr2 = new Uint8Array(mac2); + for (let i = 0; i < arr2.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + + return true; + } + + hmacFast(value: string, key: string, algorithm: "sha1" | "sha256" | "sha512"): Promise { + const hmac = forge.hmac.create(); + hmac.start(algorithm, key); + hmac.update(value); + const bytes = hmac.digest().getBytes(); + return Promise.resolve(bytes); + } + + async compareFast(a: string, b: string): Promise { + const rand = await this.randomBytes(32); + const bytes = new Uint32Array(rand); + const buffer = forge.util.createBuffer(); + for (let i = 0; i < bytes.length; i++) { + buffer.putInt32(bytes[i]); + } + const macKey = buffer.getBytes(); + + const hmac = forge.hmac.create(); + hmac.start("sha256", macKey); + hmac.update(a); + const mac1 = hmac.digest().getBytes(); + + hmac.start(null, null); + hmac.update(b); + const mac2 = hmac.digest().getBytes(); + + const equals = mac1 === mac2; + return equals; + } + + async aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ + "encrypt", + ]); + return await this.subtle.encrypt({ name: "AES-CBC", iv: iv }, impKey, data); + } + + aesDecryptFastParameters( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): DecryptParameters { + const p = new DecryptParameters(); + if (key.meta != null) { + p.encKey = key.meta.encKeyByteString; + p.macKey = key.meta.macKeyByteString; + } + + if (p.encKey == null) { + p.encKey = forge.util.decode64(key.encKeyB64); + } + p.data = forge.util.decode64(data); + p.iv = forge.util.decode64(iv); + p.macData = p.iv + p.data; + if (p.macKey == null && key.macKeyB64 != null) { + p.macKey = forge.util.decode64(key.macKeyB64); + } + if (mac != null) { + p.mac = forge.util.decode64(mac); + } + + // cache byte string keys for later + if (key.meta == null) { + key.meta = {}; + } + if (key.meta.encKeyByteString == null) { + key.meta.encKeyByteString = p.encKey; + } + if (p.macKey != null && key.meta.macKeyByteString == null) { + key.meta.macKeyByteString = p.macKey; + } + + return p; + } + + aesDecryptFast(parameters: DecryptParameters): Promise { + const dataBuffer = forge.util.createBuffer(parameters.data); + const decipher = forge.cipher.createDecipher("AES-CBC", parameters.encKey); + decipher.start({ iv: parameters.iv }); + decipher.update(dataBuffer); + decipher.finish(); + const val = decipher.output.toString(); + return Promise.resolve(val); + } + + async aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ + "decrypt", + ]); + return await this.subtle.decrypt({ name: "AES-CBC", iv: iv }, impKey, data); + } + + async rsaEncrypt( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + // Note: Edge browser requires that we specify name and hash for both key import and decrypt. + // We cannot use the proper types here. + const rsaParams = { + name: "RSA-OAEP", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + const impKey = await this.subtle.importKey("spki", publicKey, rsaParams, false, ["encrypt"]); + return await this.subtle.encrypt(rsaParams, impKey, data); + } + + async rsaDecrypt( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + // Note: Edge browser requires that we specify name and hash for both key import and decrypt. + // We cannot use the proper types here. + const rsaParams = { + name: "RSA-OAEP", + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + const impKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, false, ["decrypt"]); + return await this.subtle.decrypt(rsaParams, impKey, data); + } + + async rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const rsaParams = { + name: "RSA-OAEP", + // Have to specify some algorithm + hash: { name: this.toWebCryptoAlgorithm("sha1") }, + }; + const impPrivateKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, true, [ + "decrypt", + ]); + const jwkPrivateKey = await this.subtle.exportKey("jwk", impPrivateKey); + const jwkPublicKeyParams = { + kty: "RSA", + e: jwkPrivateKey.e, + n: jwkPrivateKey.n, + alg: "RSA-OAEP", + ext: true, + }; + const impPublicKey = await this.subtle.importKey("jwk", jwkPublicKeyParams, rsaParams, true, [ + "encrypt", + ]); + return await this.subtle.exportKey("spki", impPublicKey); + } + + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { + const rsaParams = { + name: "RSA-OAEP", + modulusLength: length, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537 + // Have to specify some algorithm + hash: { name: this.toWebCryptoAlgorithm("sha1") }, + }; + const keyPair = (await this.subtle.generateKey(rsaParams, true, [ + "encrypt", + "decrypt", + ])) as CryptoKeyPair; + const publicKey = await this.subtle.exportKey("spki", keyPair.publicKey); + const privateKey = await this.subtle.exportKey("pkcs8", keyPair.privateKey); + return [publicKey, privateKey]; + } + + randomBytes(length: number): Promise { + const arr = new Uint8Array(length); + this.crypto.getRandomValues(arr); + return Promise.resolve(arr.buffer); + } + + private toBuf(value: string | ArrayBuffer): ArrayBuffer { + let buf: ArrayBuffer; + if (typeof value === "string") { + buf = Utils.fromUtf8ToArray(value).buffer; + } else { + buf = value; + } + return buf; + } + + private toByteString(value: string | ArrayBuffer): string { + let bytes: string; + if (typeof value === "string") { + bytes = forge.util.encodeUtf8(value); + } else { + bytes = Utils.fromBufferToByteString(value); + } + return bytes; + } + + private toWebCryptoAlgorithm(algorithm: "sha1" | "sha256" | "sha512" | "md5"): string { + if (algorithm === "md5") { + throw new Error("MD5 is not supported in WebCrypto."); + } + return algorithm === "sha1" ? "SHA-1" : algorithm === "sha256" ? "SHA-256" : "SHA-512"; + } +} diff --git a/jslib/common/src/types/verification.ts b/jslib/common/src/types/verification.ts new file mode 100644 index 00000000..07ca4bbf --- /dev/null +++ b/jslib/common/src/types/verification.ts @@ -0,0 +1,6 @@ +import { VerificationType } from "../enums/verificationType"; + +export type Verification = { + type: VerificationType; + secret: string; +}; diff --git a/jslib/common/tsconfig.json b/jslib/common/tsconfig.json new file mode 100644 index 00000000..0d578efa --- /dev/null +++ b/jslib/common/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/common/tsconfig.spec.json b/jslib/common/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/common/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/electron/jest.config.js b/jslib/electron/jest.config.js new file mode 100644 index 00000000..8d59aeaf --- /dev/null +++ b/jslib/electron/jest.config.js @@ -0,0 +1,16 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testEnvironment: "jsdom", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/electron/package-lock.json b/jslib/electron/package-lock.json new file mode 100644 index 00000000..608d0561 --- /dev/null +++ b/jslib/electron/package-lock.json @@ -0,0 +1,2393 @@ +{ + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=8.6" + }, + "optionalDependencies": { + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@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==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "dependencies": { + "debug": "^4.3.2", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/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==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/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 + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/conf": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.1.tgz", + "integrity": "sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==" + }, + "node_modules/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==", + "optional": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 8.6" + } + }, + "node_modules/electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "node_modules/electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "dependencies": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "dependencies": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "node_modules/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==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dependencies": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/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==" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "dependencies": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/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==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/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==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + }, + "dependencies": { + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@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==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": 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" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "requires": { + "debug": "^4.3.2", + "sax": "^1.2.4" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "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==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "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==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "conf": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.1.tgz", + "integrity": "sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q==", + "requires": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "requires": { + "mimic-fn": "^3.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.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==" + }, + "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==", + "optional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, + "dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "requires": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + } + } + }, + "electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "requires": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + } + }, + "electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "requires": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "optional": 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==", + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "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==", + "optional": true + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "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==" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.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==", + "requires": { + "locate-path": "^3.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==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "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==", + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "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.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "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==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "optional": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + } + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "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==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "optional": true + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "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" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "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==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true + } + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^4.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/jslib/electron/package.json b/jslib/electron/package.json new file mode 100644 index 00000000..892446d5 --- /dev/null +++ b/jslib/electron/package.json @@ -0,0 +1,32 @@ +{ + "name": "@bitwarden/jslib-electron", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + } +} diff --git a/jslib/electron/spec/services/electronLog.service.spec.ts b/jslib/electron/spec/services/electronLog.service.spec.ts new file mode 100644 index 00000000..5952da43 --- /dev/null +++ b/jslib/electron/spec/services/electronLog.service.spec.ts @@ -0,0 +1,9 @@ +import { ElectronLogService } from "jslib-electron/services/electronLog.service"; + +describe("ElectronLogService", () => { + it("sets dev based on electron method", () => { + process.env.ELECTRON_IS_DEV = "1"; + const logService = new ElectronLogService(); + expect(logService).toEqual(expect.objectContaining({ isDev: true }) as any); + }); +}); diff --git a/jslib/electron/spec/test.ts b/jslib/electron/spec/test.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/electron/spec/utils.spec.ts b/jslib/electron/spec/utils.spec.ts new file mode 100644 index 00000000..f9e95a2d --- /dev/null +++ b/jslib/electron/spec/utils.spec.ts @@ -0,0 +1,27 @@ +import { cleanUserAgent } from "jslib-electron/utils"; + +const expectedUserAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome} Safari/537.36`; + +describe("cleanUserAgent", () => { + it("cleans mac agent", () => { + const initialMacAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6_0) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialMacAgent)).toEqual(expectedUserAgent); + }); + + it("cleans windows agent", () => { + const initialWindowsAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialWindowsAgent)).toEqual(expectedUserAgent); + }); + + it("cleans linux agent", () => { + const initialWindowsAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/${process.version} Chrome/${process.versions.chrome} Electron/${process.versions.electron} Safari/537.36`; + expect(cleanUserAgent(initialWindowsAgent)).toEqual(expectedUserAgent); + }); + + it("does not change version numbers", () => { + const expected = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36`; + const initialAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Bitwarden/1.28.3 Chrome/87.0.4280.141 Electron/11.4.5 Safari/537.36`; + + expect(cleanUserAgent(initialAgent)).toEqual(expected); + }); +}); diff --git a/jslib/electron/src/baseMenu.ts b/jslib/electron/src/baseMenu.ts new file mode 100644 index 00000000..09d47f7e --- /dev/null +++ b/jslib/electron/src/baseMenu.ts @@ -0,0 +1,225 @@ +import { Menu, MenuItemConstructorOptions } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { WindowMain } from "./window.main"; + +export class BaseMenu { + protected editMenuItemOptions: MenuItemConstructorOptions; + protected viewSubMenuItemOptions: MenuItemConstructorOptions[]; + protected windowMenuItemOptions: MenuItemConstructorOptions; + protected macAppMenuItemOptions: MenuItemConstructorOptions[]; + protected macWindowSubmenuOptions: MenuItemConstructorOptions[]; + + constructor(protected i18nService: I18nService, protected windowMain: WindowMain) {} + + protected initProperties() { + this.editMenuItemOptions = { + label: this.i18nService.t("edit"), + submenu: [ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ], + }; + + this.viewSubMenuItemOptions = [ + { + label: this.i18nService.t("zoomIn"), + role: "zoomIn", + accelerator: "CmdOrCtrl+=", + }, + { + label: this.i18nService.t("zoomOut"), + role: "zoomOut", + accelerator: "CmdOrCtrl+-", + }, + { + label: this.i18nService.t("resetZoom"), + role: "resetZoom", + accelerator: "CmdOrCtrl+0", + }, + { type: "separator" }, + { + label: this.i18nService.t("toggleFullScreen"), + role: "togglefullscreen", + }, + { type: "separator" }, + { + label: this.i18nService.t("reload"), + role: "forceReload", + }, + { + label: this.i18nService.t("toggleDevTools"), + role: "toggleDevTools", + accelerator: "F12", + }, + ]; + + this.windowMenuItemOptions = { + label: this.i18nService.t("window"), + role: "window", + submenu: [ + { + label: this.i18nService.t("minimize"), + role: "minimize", + }, + { + label: this.i18nService.t("close"), + role: "close", + }, + ], + }; + + if (process.platform === "darwin") { + this.macAppMenuItemOptions = [ + { + label: this.i18nService.t("services"), + role: "services", + submenu: [], + }, + { type: "separator" }, + { + label: this.i18nService.t("hideBitwarden"), + role: "hide", + }, + { + label: this.i18nService.t("hideOthers"), + role: "hideOthers", + }, + { + label: this.i18nService.t("showAll"), + role: "unhide", + }, + { type: "separator" }, + { + label: this.i18nService.t("quitBitwarden"), + role: "quit", + }, + ]; + + this.macWindowSubmenuOptions = [ + { + label: this.i18nService.t("minimize"), + role: "minimize", + }, + { + label: this.i18nService.t("zoom"), + role: "zoom", + }, + { type: "separator" }, + { + label: this.i18nService.t("bringAllToFront"), + role: "front", + }, + { + label: this.i18nService.t("close"), + role: "close", + }, + ]; + } + } + + protected initContextMenu() { + if (this.windowMain.win == null) { + return; + } + + const selectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + enabled: false, + }, + { + label: this.i18nService.t("copy"), + role: "copy", + enabled: false, + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputSelectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + this.windowMain.win.webContents.on("context-menu", (e, props) => { + const selected = props.selectionText && props.selectionText.trim() !== ""; + if (props.isEditable && selected) { + inputSelectionMenu.popup({ window: this.windowMain.win }); + } else if (props.isEditable) { + inputMenu.popup({ window: this.windowMain.win }); + } else if (selected) { + selectionMenu.popup({ window: this.windowMain.win }); + } + }); + } +} diff --git a/jslib/electron/src/globals.d.ts b/jslib/electron/src/globals.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/electron/src/services/electronCrypto.service.ts b/jslib/electron/src/services/electronCrypto.service.ts new file mode 100644 index 00000000..36ea8132 --- /dev/null +++ b/jslib/electron/src/services/electronCrypto.service.ts @@ -0,0 +1,71 @@ +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { KeySuffixOptions } from "jslib-common/enums/keySuffixOptions"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { CryptoService } from "jslib-common/services/crypto.service"; + +export class ElectronCryptoService extends CryptoService { + constructor( + cryptoFunctionService: CryptoFunctionService, + platformUtilService: PlatformUtilsService, + logService: LogService, + stateService: StateService + ) { + super(cryptoFunctionService, platformUtilService, logService, stateService); + } + + async hasKeyStored(keySuffix: KeySuffixOptions): Promise { + await this.upgradeSecurelyStoredKey(); + return super.hasKeyStored(keySuffix); + } + + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) { + await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Auto); + } + + if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Biometric); + } + } + + protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string) { + await this.upgradeSecurelyStoredKey(); + return super.retrieveKeyFromStorage(keySuffix, userId); + } + + /** + * @deprecated 4 Jun 2021 This is temporary upgrade method to move from a single shared stored key to + * multiple, unique stored keys for each use, e.g. never logout vs. biometric authentication. + */ + private async upgradeSecurelyStoredKey() { + // attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway. + const key = await this.stateService.getCryptoMasterKeyB64(); + + if (key == null) { + return; + } + + try { + if (await this.shouldStoreKey(KeySuffixOptions.Auto)) { + await this.stateService.setCryptoMasterKeyAuto(key); + } + if (await this.shouldStoreKey(KeySuffixOptions.Biometric)) { + await this.stateService.setCryptoMasterKeyBiometric(key); + } + } catch (e) { + this.logService.error( + `Encountered error while upgrading obsolete Bitwarden secure storage item:` + ); + this.logService.error(e); + } + + await this.stateService.setCryptoMasterKeyB64(null); + } +} diff --git a/jslib/electron/src/services/electronLog.service.ts b/jslib/electron/src/services/electronLog.service.ts new file mode 100644 index 00000000..c6195416 --- /dev/null +++ b/jslib/electron/src/services/electronLog.service.ts @@ -0,0 +1,45 @@ +import * as path from "path"; + +import log from "electron-log"; + +import { LogLevelType } from "jslib-common/enums/logLevelType"; +import { ConsoleLogService as BaseLogService } from "jslib-common/services/consoleLog.service"; + +import { isDev } from "../utils"; + +export class ElectronLogService extends BaseLogService { + constructor(protected filter: (level: LogLevelType) => boolean = null, logDir: string = null) { + super(isDev(), filter); + if (log.transports == null) { + return; + } + + log.transports.file.level = "info"; + if (logDir != null) { + log.transports.file.file = path.join(logDir, "app.log"); + } + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + switch (level) { + case LogLevelType.Debug: + log.debug(message); + break; + case LogLevelType.Info: + log.info(message); + break; + case LogLevelType.Warning: + log.warn(message); + break; + case LogLevelType.Error: + log.error(message); + break; + default: + break; + } + } +} diff --git a/jslib/electron/src/services/electronMainMessaging.service.ts b/jslib/electron/src/services/electronMainMessaging.service.ts new file mode 100644 index 00000000..f79a32b4 --- /dev/null +++ b/jslib/electron/src/services/electronMainMessaging.service.ts @@ -0,0 +1,65 @@ +import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme } from "electron"; + +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { ThemeType } from "jslib-common/enums/themeType"; + +import { RendererMenuItem } from "../utils"; +import { WindowMain } from "../window.main"; + +export class ElectronMainMessagingService implements MessagingService { + constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { + ipcMain.handle("appVersion", () => { + return app.getVersion(); + }); + + ipcMain.handle("systemTheme", () => { + return nativeTheme.shouldUseDarkColors ? ThemeType.Dark : ThemeType.Light; + }); + + ipcMain.handle("showMessageBox", (event, options) => { + return dialog.showMessageBox(this.windowMain.win, options); + }); + + ipcMain.handle("openContextMenu", (event, options: { menu: RendererMenuItem[] }) => { + return new Promise((resolve) => { + const menu = new Menu(); + options.menu.forEach((m, index) => { + menu.append( + new MenuItem({ + label: m.label, + type: m.type, + click: () => { + resolve(index); + }, + }) + ); + }); + menu.popup({ + window: windowMain.win, + callback: () => { + resolve(-1); + }, + }); + }); + }); + + ipcMain.handle("windowVisible", () => { + return windowMain.win?.isVisible(); + }); + + nativeTheme.on("updated", () => { + windowMain.win?.webContents.send( + "systemThemeUpdated", + nativeTheme.shouldUseDarkColors ? ThemeType.Dark : ThemeType.Light + ); + }); + } + + send(subscriber: string, arg: any = {}) { + const message = Object.assign({}, { command: subscriber }, arg); + this.onMessage(message); + if (this.windowMain.win != null) { + this.windowMain.win.webContents.send("messagingService", message); + } + } +} diff --git a/jslib/electron/src/services/electronPlatformUtils.service.ts b/jslib/electron/src/services/electronPlatformUtils.service.ts new file mode 100644 index 00000000..c80218d2 --- /dev/null +++ b/jslib/electron/src/services/electronPlatformUtils.service.ts @@ -0,0 +1,217 @@ +import { clipboard, ipcRenderer, shell } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { ClientType } from "jslib-common/enums/clientType"; +import { DeviceType } from "jslib-common/enums/deviceType"; +import { ThemeType } from "jslib-common/enums/themeType"; + +import { isDev, isMacAppStore } from "../utils"; + +export class ElectronPlatformUtilsService implements PlatformUtilsService { + private clientType: ClientType; + private deviceCache: DeviceType = null; + + constructor( + protected i18nService: I18nService, + private messagingService: MessagingService, + private isDesktopApp: boolean, + private stateService: StateService + ) { + this.clientType = isDesktopApp ? ClientType.Desktop : ClientType.DirectoryConnector; + } + + getDevice(): DeviceType { + if (!this.deviceCache) { + switch (process.platform) { + case "win32": + this.deviceCache = DeviceType.WindowsDesktop; + break; + case "darwin": + this.deviceCache = DeviceType.MacOsDesktop; + break; + case "linux": + default: + this.deviceCache = DeviceType.LinuxDesktop; + break; + } + } + + return this.deviceCache; + } + + getDeviceString(): string { + const device = DeviceType[this.getDevice()].toLowerCase(); + return device.replace("desktop", ""); + } + + getClientType() { + return this.clientType; + } + + isFirefox(): boolean { + return false; + } + + isChrome(): boolean { + return true; + } + + isEdge(): boolean { + return false; + } + + isOpera(): boolean { + return false; + } + + isVivaldi(): boolean { + return false; + } + + isSafari(): boolean { + return false; + } + + isMacAppStore(): boolean { + return isMacAppStore(); + } + + isViewOpen(): Promise { + return Promise.resolve(false); + } + + launchUri(uri: string, options?: any): void { + shell.openExternal(uri); + } + + saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void { + const blob = new Blob([blobData], blobOptions); + const a = win.document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = fileName; + win.document.body.appendChild(a); + a.click(); + win.document.body.removeChild(a); + } + + getApplicationVersion(): Promise { + return ipcRenderer.invoke("appVersion"); + } + + // Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349 + // has been merged and an updated electron build is available. + supportsWebAuthn(win: Window): boolean { + return process.platform === "win32"; + } + + supportsDuo(): boolean { + return true; + } + + showToast( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: any + ): void { + this.messagingService.send("showToast", { + text: text, + title: title, + type: type, + options: options, + }); + } + + async showDialog( + text: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string + ): Promise { + const buttons = [confirmText == null ? this.i18nService.t("ok") : confirmText]; + if (cancelText != null) { + buttons.push(cancelText); + } + + const result = await ipcRenderer.invoke("showMessageBox", { + type: type, + title: title, + message: title, + detail: text, + buttons: buttons, + cancelId: buttons.length === 2 ? 1 : null, + defaultId: 0, + noLink: true, + }); + + return Promise.resolve(result.response === 0); + } + + isDev(): boolean { + return isDev(); + } + + isSelfHost(): boolean { + return false; + } + + copyToClipboard(text: string, options?: any): void { + const type = options ? options.type : null; + const clearing = options ? !!options.clearing : false; + const clearMs: number = options && options.clearMs ? options.clearMs : null; + clipboard.writeText(text, type); + if (!clearing) { + this.messagingService.send("copiedToClipboard", { + clipboardValue: text, + clearMs: clearMs, + type: type, + clearing: clearing, + }); + } + } + + readFromClipboard(options?: any): Promise { + const type = options ? options.type : null; + return Promise.resolve(clipboard.readText(type)); + } + + async supportsBiometric(): Promise { + return await this.stateService.getEnableBiometric(); + } + + authenticateBiometric(): Promise { + return new Promise((resolve) => { + const val = ipcRenderer.sendSync("biometric", { + action: "authenticate", + }); + resolve(val); + }); + } + + getDefaultSystemTheme() { + return ipcRenderer.invoke("systemTheme"); + } + + onDefaultSystemThemeChange(callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown) { + ipcRenderer.on("systemThemeUpdated", (event, theme: ThemeType.Light | ThemeType.Dark) => + callback(theme) + ); + } + + async getEffectiveTheme() { + const theme = await this.stateService.getTheme(); + if (theme == null || theme === ThemeType.System) { + return this.getDefaultSystemTheme(); + } else { + return theme; + } + } + + supportsSecureStorage(): boolean { + return true; + } +} diff --git a/jslib/electron/src/services/electronRendererMessaging.service.ts b/jslib/electron/src/services/electronRendererMessaging.service.ts new file mode 100644 index 00000000..89350a65 --- /dev/null +++ b/jslib/electron/src/services/electronRendererMessaging.service.ts @@ -0,0 +1,26 @@ +import { ipcRenderer } from "electron"; + +import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service"; +import { MessagingService } from "jslib-common/abstractions/messaging.service"; + +export class ElectronRendererMessagingService implements MessagingService { + constructor(private broadcasterService: BroadcasterService) { + ipcRenderer.on("messagingService", async (event: any, message: any) => { + if (message.command) { + this.sendMessage(message.command, message, false); + } + }); + } + + send(subscriber: string, arg: any = {}) { + this.sendMessage(subscriber, arg, true); + } + + private sendMessage(subscriber: string, arg: any = {}, toMain: boolean) { + const message = Object.assign({}, { command: subscriber }, arg); + this.broadcasterService.send(message); + if (toMain) { + ipcRenderer.send("messagingService", message); + } + } +} diff --git a/jslib/electron/src/services/electronRendererSecureStorage.service.ts b/jslib/electron/src/services/electronRendererSecureStorage.service.ts new file mode 100644 index 00000000..5c3f3c74 --- /dev/null +++ b/jslib/electron/src/services/electronRendererSecureStorage.service.ts @@ -0,0 +1,43 @@ +import { ipcRenderer } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { StorageOptions } from "jslib-common/models/domain/storageOptions"; + +export class ElectronRendererSecureStorageService implements StorageService { + async get(key: string, options?: StorageOptions): Promise { + const val = ipcRenderer.sendSync("keytar", { + action: "getPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(val != null ? (JSON.parse(val) as T) : null); + } + + async has(key: string, options?: StorageOptions): Promise { + const val = ipcRenderer.sendSync("keytar", { + action: "hasPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(!!val); + } + + async save(key: string, obj: any, options?: StorageOptions): Promise { + ipcRenderer.sendSync("keytar", { + action: "setPassword", + key: key, + keySuffix: options?.keySuffix ?? "", + value: JSON.stringify(obj), + }); + return Promise.resolve(); + } + + async remove(key: string, options?: StorageOptions): Promise { + ipcRenderer.sendSync("keytar", { + action: "deletePassword", + key: key, + keySuffix: options?.keySuffix ?? "", + }); + return Promise.resolve(); + } +} diff --git a/jslib/electron/src/services/electronRendererStorage.service.ts b/jslib/electron/src/services/electronRendererStorage.service.ts new file mode 100644 index 00000000..69436bfe --- /dev/null +++ b/jslib/electron/src/services/electronRendererStorage.service.ts @@ -0,0 +1,34 @@ +import { ipcRenderer } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; + +export class ElectronRendererStorageService implements StorageService { + get(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "get", + key: key, + }); + } + + has(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "has", + key: key, + }); + } + + save(key: string, obj: any): Promise { + return ipcRenderer.invoke("storageService", { + action: "save", + key: key, + obj: obj, + }); + } + + remove(key: string): Promise { + return ipcRenderer.invoke("storageService", { + action: "remove", + key: key, + }); + } +} diff --git a/jslib/electron/src/services/electronStorage.service.ts b/jslib/electron/src/services/electronStorage.service.ts new file mode 100644 index 00000000..fdabf2a2 --- /dev/null +++ b/jslib/electron/src/services/electronStorage.service.ts @@ -0,0 +1,60 @@ +import * as fs from "fs"; + +import { ipcMain } from "electron"; + +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; + +// eslint-disable-next-line +const Store = require("electron-store"); + +export class ElectronStorageService implements StorageService { + private store: any; + + constructor(dir: string, defaults = {}) { + if (!fs.existsSync(dir)) { + NodeUtils.mkdirpSync(dir, "700"); + } + const storeConfig: any = { + defaults: defaults, + name: "data", + }; + this.store = new Store(storeConfig); + + ipcMain.handle("storageService", (event, options) => { + switch (options.action) { + case "get": + return this.get(options.key); + case "has": + return this.has(options.key); + case "save": + return this.save(options.key, options.obj); + case "remove": + return this.remove(options.key); + } + }); + } + + get(key: string): Promise { + const val = this.store.get(key) as T; + return Promise.resolve(val != null ? val : null); + } + + has(key: string): Promise { + const val = this.store.get(key); + return Promise.resolve(val != null); + } + + save(key: string, obj: any): Promise { + if (obj instanceof Set) { + obj = Array.from(obj); + } + this.store.set(key, obj); + return Promise.resolve(); + } + + remove(key: string): Promise { + this.store.delete(key); + return Promise.resolve(); + } +} diff --git a/jslib/electron/src/tray.main.ts b/jslib/electron/src/tray.main.ts new file mode 100644 index 00000000..ebbc5687 --- /dev/null +++ b/jslib/electron/src/tray.main.ts @@ -0,0 +1,186 @@ +import * as path from "path"; + +import { app, BrowserWindow, Menu, MenuItemConstructorOptions, nativeImage, Tray } from "electron"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { StateService } from "jslib-common/abstractions/state.service"; + +import { WindowMain } from "./window.main"; + +export class TrayMain { + contextMenu: Menu; + + private appName: string; + private tray: Tray; + private icon: string | Electron.NativeImage; + private pressedIcon: Electron.NativeImage; + + constructor( + private windowMain: WindowMain, + private i18nService: I18nService, + private stateService: StateService + ) { + if (process.platform === "win32") { + this.icon = path.join(__dirname, "/images/icon.ico"); + } else if (process.platform === "darwin") { + const nImage = nativeImage.createFromPath(path.join(__dirname, "/images/icon-template.png")); + nImage.setTemplateImage(true); + this.icon = nImage; + this.pressedIcon = nativeImage.createFromPath( + path.join(__dirname, "/images/icon-highlight.png") + ); + } else { + this.icon = path.join(__dirname, "/images/icon.png"); + } + } + + async init(appName: string, additionalMenuItems: MenuItemConstructorOptions[] = null) { + this.appName = appName; + + const menuItemOptions: MenuItemConstructorOptions[] = [ + { + label: this.i18nService.t("showHide"), + click: () => this.toggleWindow(), + }, + { type: "separator" }, + { + label: this.i18nService.t("exit"), + click: () => this.closeWindow(), + }, + ]; + + if (additionalMenuItems != null) { + menuItemOptions.splice(1, 0, ...additionalMenuItems); + } + + this.contextMenu = Menu.buildFromTemplate(menuItemOptions); + if (await this.stateService.getEnableTray()) { + this.showTray(); + } + } + + setupWindowListeners(win: BrowserWindow) { + win.on("minimize", async (e: Event) => { + if (await this.stateService.getEnableMinimizeToTray()) { + e.preventDefault(); + this.hideToTray(); + } + }); + + win.on("close", async (e: Event) => { + if (await this.stateService.getEnableCloseToTray()) { + if (!this.windowMain.isQuitting) { + e.preventDefault(); + this.hideToTray(); + } + } + }); + + win.on("show", async () => { + const enableTray = await this.stateService.getEnableTray(); + if (!enableTray) { + setTimeout(() => this.removeTray(false), 100); + } + }); + } + + removeTray(showWindow = true) { + // Due to https://github.com/electron/electron/issues/17622 + // we cannot destroy the tray icon on linux. + if (this.tray != null && process.platform !== "linux") { + this.tray.destroy(); + this.tray = null; + } + + if (showWindow && this.windowMain.win != null && !this.windowMain.win.isVisible()) { + this.windowMain.win.show(); + } + } + + async hideToTray() { + this.showTray(); + if (this.windowMain.win != null) { + this.windowMain.win.hide(); + } + if (this.isDarwin() && !(await this.stateService.getAlwaysShowDock())) { + this.hideDock(); + } + } + + restoreFromTray() { + if (this.windowMain.win == null || !this.windowMain.win.isVisible()) { + this.toggleWindow(); + } + } + + showTray() { + if (this.tray != null) { + return; + } + + this.tray = new Tray(this.icon); + this.tray.setToolTip(this.appName); + this.tray.on("click", () => this.toggleWindow()); + this.tray.on("right-click", () => this.tray.popUpContextMenu(this.contextMenu)); + + if (this.pressedIcon != null) { + this.tray.setPressedImage(this.pressedIcon); + } + if (this.contextMenu != null && !this.isDarwin()) { + this.tray.setContextMenu(this.contextMenu); + } + } + + updateContextMenu() { + if (this.contextMenu != null && this.isLinux()) { + this.tray.setContextMenu(this.contextMenu); + } + } + + private hideDock() { + app.dock.hide(); + } + + private showDock() { + app.dock.show(); + } + + private isDarwin() { + return process.platform === "darwin"; + } + + private isLinux() { + return process.platform === "linux"; + } + + private async toggleWindow() { + if (this.windowMain.win == null) { + if (this.isDarwin()) { + // On MacOS, closing the window via the red button destroys the BrowserWindow instance. + this.windowMain.createWindow().then(() => { + this.windowMain.win.show(); + this.showDock(); + }); + } + return; + } + if (this.windowMain.win.isVisible()) { + this.windowMain.win.hide(); + if (this.isDarwin() && !(await this.stateService.getAlwaysShowDock())) { + this.hideDock(); + } + } else { + this.windowMain.win.show(); + if (this.isDarwin()) { + this.showDock(); + } + } + } + + private closeWindow() { + this.windowMain.isQuitting = true; + if (this.windowMain.win != null) { + this.windowMain.win.close(); + } + } +} diff --git a/jslib/electron/src/updater.main.ts b/jslib/electron/src/updater.main.ts new file mode 100644 index 00000000..881208f8 --- /dev/null +++ b/jslib/electron/src/updater.main.ts @@ -0,0 +1,156 @@ +import { dialog, shell } from "electron"; +import log from "electron-log"; +import { autoUpdater } from "electron-updater"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "./utils"; +import { WindowMain } from "./window.main"; + +const UpdaterCheckInitalDelay = 5 * 1000; // 5 seconds +const UpdaterCheckInterval = 12 * 60 * 60 * 1000; // 12 hours + +export class UpdaterMain { + private doingUpdateCheck = false; + private doingUpdateCheckWithFeedback = false; + private canUpdate = false; + + constructor( + private i18nService: I18nService, + private windowMain: WindowMain, + private gitHubProject: string, + private onCheckingForUpdate: () => void = null, + private onReset: () => void = null, + private onUpdateDownloaded: () => void = null, + private projectName: string + ) { + autoUpdater.logger = log; + + const linuxCanUpdate = process.platform === "linux" && isAppImage(); + const windowsCanUpdate = + process.platform === "win32" && !isWindowsStore() && !isWindowsPortable(); + const macCanUpdate = process.platform === "darwin" && !isMacAppStore(); + this.canUpdate = + process.env.ELECTRON_NO_UPDATER !== "1" && + (linuxCanUpdate || windowsCanUpdate || macCanUpdate); + } + + async init() { + global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitalDelay); + global.setInterval(async () => await this.checkForUpdate(), UpdaterCheckInterval); + + autoUpdater.on("checking-for-update", () => { + if (this.onCheckingForUpdate != null) { + this.onCheckingForUpdate(); + } + this.doingUpdateCheck = true; + }); + + autoUpdater.on("update-available", async () => { + if (this.doingUpdateCheckWithFeedback) { + if (this.windowMain.win == null) { + this.reset(); + return; + } + + const result = await dialog.showMessageBox(this.windowMain.win, { + type: "info", + title: + this.i18nService.t(this.projectName) + " - " + this.i18nService.t("updateAvailable"), + message: this.i18nService.t("updateAvailable"), + detail: this.i18nService.t("updateAvailableDesc"), + buttons: [this.i18nService.t("yes"), this.i18nService.t("no")], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + + if (result.response === 0) { + autoUpdater.downloadUpdate(); + } else { + this.reset(); + } + } + }); + + autoUpdater.on("update-not-available", () => { + if (this.doingUpdateCheckWithFeedback && this.windowMain.win != null) { + dialog.showMessageBox(this.windowMain.win, { + message: this.i18nService.t("noUpdatesAvailable"), + buttons: [this.i18nService.t("ok")], + defaultId: 0, + noLink: true, + }); + } + + this.reset(); + }); + + autoUpdater.on("update-downloaded", async (info) => { + if (this.onUpdateDownloaded != null) { + this.onUpdateDownloaded(); + } + + if (this.windowMain.win == null) { + return; + } + + const result = await dialog.showMessageBox(this.windowMain.win, { + type: "info", + title: this.i18nService.t(this.projectName) + " - " + this.i18nService.t("restartToUpdate"), + message: this.i18nService.t("restartToUpdate"), + detail: this.i18nService.t("restartToUpdateDesc", info.version), + buttons: [this.i18nService.t("restart"), this.i18nService.t("later")], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + + if (result.response === 0) { + // Quit and install have a different window logic, setting `isQuitting` just to be safe. + this.windowMain.isQuitting = true; + autoUpdater.quitAndInstall(true, true); + } + }); + + autoUpdater.on("error", (error) => { + if (this.doingUpdateCheckWithFeedback) { + dialog.showErrorBox( + this.i18nService.t("updateError"), + error == null ? this.i18nService.t("unknown") : (error.stack || error).toString() + ); + } + + this.reset(); + }); + } + + async checkForUpdate(withFeedback = false) { + if (this.doingUpdateCheck || isDev()) { + return; + } + + if (!this.canUpdate) { + if (withFeedback) { + shell.openExternal("https://github.com/bitwarden/" + this.gitHubProject + "/releases"); + } + + return; + } + + this.doingUpdateCheckWithFeedback = withFeedback; + if (withFeedback) { + autoUpdater.autoDownload = false; + } + + await autoUpdater.checkForUpdates(); + } + + private reset() { + if (this.onReset != null) { + this.onReset(); + } + autoUpdater.autoDownload = true; + this.doingUpdateCheck = false; + } +} diff --git a/jslib/electron/src/utils.ts b/jslib/electron/src/utils.ts new file mode 100644 index 00000000..74a40773 --- /dev/null +++ b/jslib/electron/src/utils.ts @@ -0,0 +1,76 @@ +import { ipcRenderer } from "electron"; + +export type RendererMenuItem = { + label?: string; + type?: "normal" | "separator" | "submenu" | "checkbox" | "radio"; + click?: () => any; +}; + +export function invokeMenu(menu: RendererMenuItem[]) { + const menuWithoutClick = menu.map((m) => { + return { label: m.label, type: m.type }; + }); + ipcRenderer.invoke("openContextMenu", { menu: menuWithoutClick }).then((i: number) => { + if (i !== -1) { + menu[i].click(); + } + }); +} + +export function isDev() { + // ref: https://github.com/sindresorhus/electron-is-dev + if ("ELECTRON_IS_DEV" in process.env) { + return parseInt(process.env.ELECTRON_IS_DEV, 10) === 1; + } + return process.defaultApp || /node_modules[\\/]electron[\\/]/.test(process.execPath); +} + +export function isAppImage() { + return process.platform === "linux" && "APPIMAGE" in process.env; +} + +export function isMac() { + return process.platform === "darwin"; +} + +export function isMacAppStore() { + return isMac() && process.mas === true; +} + +export function isWindowsStore() { + const isWindows = process.platform === "win32"; + let windowsStore = process.windowsStore; + if ( + isWindows && + !windowsStore && + process.resourcesPath.indexOf("8bitSolutionsLLC.bitwardendesktop_") > -1 + ) { + windowsStore = true; + } + return isWindows && windowsStore === true; +} + +export function isSnapStore() { + return process.platform === "linux" && process.env.SNAP_USER_DATA != null; +} + +export function isWindowsPortable() { + return process.platform === "win32" && process.env.PORTABLE_EXECUTABLE_DIR != null; +} + +/** + * Sanitize user agent so external resources used by the app can't built data on our users. + */ +export function cleanUserAgent(userAgent: string): string { + const userAgentItem = (startString: string, endString: string) => { + const startIndex = userAgent.indexOf(startString); + return userAgent.substring(startIndex, userAgent.indexOf(endString, startIndex) + 1); + }; + const systemInformation = "(Windows NT 10.0; Win64; x64)"; + + // Set system information, remove bitwarden, and electron information + return userAgent + .replace(userAgentItem("(", ")"), systemInformation) + .replace(userAgentItem("Bitwarden", " "), "") + .replace(userAgentItem("Electron", " "), ""); +} diff --git a/jslib/electron/src/window.main.ts b/jslib/electron/src/window.main.ts new file mode 100644 index 00000000..66d4c780 --- /dev/null +++ b/jslib/electron/src/window.main.ts @@ -0,0 +1,293 @@ +import * as path from "path"; +import * as url from "url"; + +import { app, BrowserWindow, screen } from "electron"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { StateService } from "jslib-common/abstractions/state.service"; + +import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "./utils"; + +const mainWindowSizeKey = "mainWindowSize"; +const WindowEventHandlingDelay = 100; +export class WindowMain { + win: BrowserWindow; + isQuitting = false; + + private windowStateChangeTimer: NodeJS.Timer; + private windowStates: { [key: string]: any } = {}; + private enableAlwaysOnTop = false; + + constructor( + private stateService: StateService, + private logService: LogService, + private hideTitleBar = false, + private defaultWidth = 950, + private defaultHeight = 600, + private argvCallback: (argv: string[]) => void = null, + private createWindowCallback: (win: BrowserWindow) => void + ) {} + + init(): Promise { + return new Promise((resolve, reject) => { + try { + if (!isMacAppStore() && !isSnapStore()) { + const gotTheLock = app.requestSingleInstanceLock(); + if (!gotTheLock) { + app.quit(); + return; + } else { + // eslint-disable-next-line + app.on("second-instance", (event, argv, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (this.win != null) { + if (this.win.isMinimized() || !this.win.isVisible()) { + this.win.show(); + } + this.win.focus(); + } + if (process.platform === "win32" || process.platform === "linux") { + if (this.argvCallback != null) { + this.argvCallback(argv); + } + } + }); + } + } + + // This method will be called when Electron is shutting + // down the application. + app.on("before-quit", () => { + this.isQuitting = true; + }); + + // This method will be called when Electron has finished + // initialization and is ready to create browser windows. + // Some APIs can only be used after this event occurs. + app.on("ready", async () => { + await this.createWindow(); + resolve(); + if (this.argvCallback != null) { + this.argvCallback(process.argv); + } + }); + + // Quit when all windows are closed. + app.on("window-all-closed", () => { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== "darwin" || this.isQuitting || isMacAppStore()) { + app.quit(); + } + }); + + app.on("activate", async () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (this.win === null) { + await this.createWindow(); + } else { + // Show the window when clicking on Dock icon + this.win.show(); + } + }); + } catch (e) { + // Catch Error + // throw e; + reject(e); + } + }); + } + + async createWindow(): Promise { + this.windowStates[mainWindowSizeKey] = await this.getWindowState( + this.defaultWidth, + this.defaultHeight + ); + this.enableAlwaysOnTop = await this.stateService.getEnableAlwaysOnTop(); + + // Create the browser window. + this.win = new BrowserWindow({ + width: this.windowStates[mainWindowSizeKey].width, + height: this.windowStates[mainWindowSizeKey].height, + minWidth: 680, + minHeight: 500, + x: this.windowStates[mainWindowSizeKey].x, + y: this.windowStates[mainWindowSizeKey].y, + title: app.name, + icon: process.platform === "linux" ? path.join(__dirname, "/images/icon.png") : undefined, + titleBarStyle: this.hideTitleBar && process.platform === "darwin" ? "hiddenInset" : undefined, + show: false, + backgroundColor: "#fff", + alwaysOnTop: this.enableAlwaysOnTop, + webPreferences: { + spellcheck: false, + nodeIntegration: true, + backgroundThrottling: false, + contextIsolation: false, + }, + }); + + if (this.windowStates[mainWindowSizeKey].isMaximized) { + this.win.maximize(); + } + + // Show it later since it might need to be maximized. + this.win.show(); + + // and load the index.html of the app. + this.win.loadURL( + url.format({ + protocol: "file:", + pathname: path.join(__dirname, "/index.html"), + slashes: true, + }), + { + userAgent: cleanUserAgent(this.win.webContents.userAgent), + } + ); + + // Open the DevTools. + if (isDev()) { + this.win.webContents.openDevTools(); + } + + // Emitted when the window is closed. + this.win.on("closed", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + + // Dereference the window object, usually you would store window + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + this.win = null; + }); + + this.win.on("close", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("maximize", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("unmaximize", async () => { + await this.updateWindowState(mainWindowSizeKey, this.win); + }); + + this.win.on("resize", () => { + this.windowStateChangeHandler(mainWindowSizeKey, this.win); + }); + + this.win.on("move", () => { + this.windowStateChangeHandler(mainWindowSizeKey, this.win); + }); + this.win.on("focus", () => { + this.win.webContents.send("messagingService", { + command: "windowIsFocused", + windowIsFocused: true, + }); + }); + + if (this.createWindowCallback) { + this.createWindowCallback(this.win); + } + } + + async toggleAlwaysOnTop() { + this.enableAlwaysOnTop = !this.win.isAlwaysOnTop(); + this.win.setAlwaysOnTop(this.enableAlwaysOnTop); + await this.stateService.setEnableAlwaysOnTop(this.enableAlwaysOnTop); + } + + private windowStateChangeHandler(configKey: string, win: BrowserWindow) { + global.clearTimeout(this.windowStateChangeTimer); + this.windowStateChangeTimer = global.setTimeout(async () => { + await this.updateWindowState(configKey, win); + }, WindowEventHandlingDelay); + } + + private async updateWindowState(configKey: string, win: BrowserWindow) { + if (win == null) { + return; + } + + try { + const bounds = win.getBounds(); + + if (this.windowStates[configKey] == null) { + this.windowStates[configKey] = await this.stateService.getWindow(); + if (this.windowStates[configKey] == null) { + this.windowStates[configKey] = {}; + } + } + + this.windowStates[configKey].isMaximized = win.isMaximized(); + this.windowStates[configKey].displayBounds = screen.getDisplayMatching(bounds).bounds; + + if (!win.isMaximized() && !win.isMinimized() && !win.isFullScreen()) { + this.windowStates[configKey].x = bounds.x; + this.windowStates[configKey].y = bounds.y; + this.windowStates[configKey].width = bounds.width; + this.windowStates[configKey].height = bounds.height; + } + + await this.stateService.setWindow(this.windowStates[configKey]); + } catch (e) { + this.logService.error(e); + } + } + + private async getWindowState(defaultWidth: number, defaultHeight: number) { + const state = await this.stateService.getWindow(); + + const isValid = state != null && (this.stateHasBounds(state) || state.isMaximized); + let displayBounds: Electron.Rectangle = null; + if (!isValid) { + state.width = defaultWidth; + state.height = defaultHeight; + + displayBounds = screen.getPrimaryDisplay().bounds; + } else if (this.stateHasBounds(state) && state.displayBounds) { + // Check if the display where the window was last open is still available + displayBounds = screen.getDisplayMatching(state.displayBounds).bounds; + + if ( + displayBounds.width !== state.displayBounds.width || + displayBounds.height !== state.displayBounds.height || + displayBounds.x !== state.displayBounds.x || + displayBounds.y !== state.displayBounds.y + ) { + state.x = undefined; + state.y = undefined; + displayBounds = screen.getPrimaryDisplay().bounds; + } + } + + if (displayBounds != null) { + if (state.width > displayBounds.width && state.height > displayBounds.height) { + state.isMaximized = true; + } + + if (state.width > displayBounds.width) { + state.width = displayBounds.width - 10; + } + if (state.height > displayBounds.height) { + state.height = displayBounds.height - 10; + } + } + + return state; + } + + private stateHasBounds(state: any): boolean { + return ( + state != null && + Number.isInteger(state.x) && + Number.isInteger(state.y) && + Number.isInteger(state.width) && + state.width > 0 && + Number.isInteger(state.height) && + state.height > 0 + ); + } +} diff --git a/jslib/electron/tsconfig.json b/jslib/electron/tsconfig.json new file mode 100644 index 00000000..d5501ab3 --- /dev/null +++ b/jslib/electron/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-electron/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/electron/tsconfig.spec.json b/jslib/electron/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/electron/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/jest.config.js b/jslib/jest.config.js new file mode 100644 index 00000000..fabe9db7 --- /dev/null +++ b/jslib/jest.config.js @@ -0,0 +1,18 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), + projects: [ + "/angular/jest.config.js", + "/common/jest.config.js", + "/electron/jest.config.js", + "/node/jest.config.js", + ], +}; diff --git a/jslib/node/jest.config.js b/jslib/node/jest.config.js new file mode 100644 index 00000000..f1bfb7a9 --- /dev/null +++ b/jslib/node/jest.config.js @@ -0,0 +1,15 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); + +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testMatch: ["**/+(*.)+(spec).+(ts)"], + setupFilesAfterEnv: ["/spec/test.ts"], + collectCoverage: true, + coverageReporters: ["html", "lcov"], + coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/jslib/node/package-lock.json b/jslib/node/package-lock.json new file mode 100644 index 00000000..ab1b64a3 --- /dev/null +++ b/jslib/node/package-lock.json @@ -0,0 +1,1183 @@ +{ + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "../common": { + "name": "@bitwarden/jslib-common", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^0.10.0", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^0.9.7", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "../common", + "link": true + }, + "node_modules/@types/inquirer": { + "version": "7.3.3", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/lodash": { + "version": "4.14.175", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, + "node_modules/@types/lowdb": { + "version": "1.0.11", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.5.12", + "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/through": { + "version": "0.0.30", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.0.0", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lowdb": { + "version": "1.0.0", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "dependencies": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.50.0", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.33", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dependencies": { + "mime-db": "1.50.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/node-fetch": { + "version": "2.6.5", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + }, + "node_modules/steno": { + "version": "0.4.4", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "dependencies": { + "graceful-fs": "^4.1.3" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/tmp": { + "version": "0.0.33", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/tslib": { + "version": "1.14.1", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + }, + "dependencies": { + "@bitwarden/jslib-common": { + "version": "file:../common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^0.9.7", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^0.10.0", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@types/inquirer": { + "version": "7.3.3", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/lodash": { + "version": "4.14.175", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, + "@types/lowdb": { + "version": "1.0.11", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/node": { + "version": "16.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", + "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.5.12", + "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/through": { + "version": "0.0.30", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "agent-base": { + "version": "6.0.2", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "asynckit": { + "version": "0.4.0", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "balanced-match": { + "version": "1.0.2", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "cli-cursor": { + "version": "3.1.0", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "color-convert": { + "version": "2.0.1", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "concat-map": { + "version": "0.0.1", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "debug": { + "version": "4.3.2", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "emoji-regex": { + "version": "8.0.0", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "external-editor": { + "version": "3.1.0", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "form-data": { + "version": "4.0.0", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + }, + "graceful-fs": { + "version": "4.2.8", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "has-flag": { + "version": "4.0.0", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "https-proxy-agent": { + "version": "5.0.0", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "8.0.0", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-promise": { + "version": "2.2.2", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "lodash": { + "version": "4.17.21", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lowdb": { + "version": "1.0.0", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "requires": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + } + }, + "mime-db": { + "version": "1.50.0", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" + }, + "mime-types": { + "version": "2.1.33", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "requires": { + "mime-db": "1.50.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.0.4", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.8", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node-fetch": { + "version": "2.6.5", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "path-is-absolute": { + "version": "1.0.1", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "restore-cursor": { + "version": "3.1.0", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "rxjs": { + "version": "6.6.7", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "signal-exit": { + "version": "3.0.5", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==" + }, + "steno": { + "version": "0.4.4", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "requires": { + "graceful-fs": "^4.1.3" + } + }, + "string-width": { + "version": "4.2.3", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.33", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tr46": { + "version": "0.0.3", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "tslib": { + "version": "1.14.1", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "type-fest": { + "version": "0.21.3", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/jslib/node/package.json b/jslib/node/package.json new file mode 100644 index 00000000..b5c6c225 --- /dev/null +++ b/jslib/node/package.json @@ -0,0 +1,38 @@ +{ + "name": "@bitwarden/jslib-node", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + } +} diff --git a/jslib/node/spec/cli/consoleLog.service.spec.ts b/jslib/node/spec/cli/consoleLog.service.spec.ts new file mode 100644 index 00000000..3c76d5a6 --- /dev/null +++ b/jslib/node/spec/cli/consoleLog.service.spec.ts @@ -0,0 +1,45 @@ +import { + interceptConsole, + restoreConsole, +} from "jslib-common/../spec/services/consolelog.service.spec"; + +import { ConsoleLogService } from "jslib-node/cli/services/consoleLog.service"; + +let caughtMessage: any = {}; + +describe("CLI Console log service", () => { + let logService: ConsoleLogService; + beforeEach(() => { + caughtMessage = {}; + interceptConsole(caughtMessage); + logService = new ConsoleLogService(true); + }); + + afterAll(() => { + restoreConsole(); + }); + + it("should redirect all console to error if BW_RESPONSE env is true", () => { + process.env.BW_RESPONSE = "true"; + + logService.debug("this is a debug message"); + expect(caughtMessage).toMatchObject({ + error: { 0: "this is a debug message" }, + }); + }); + + it("should not redirect console to error if BW_RESPONSE != true", () => { + process.env.BW_RESPONSE = "false"; + + logService.debug("debug"); + logService.info("info"); + logService.warning("warning"); + logService.error("error"); + + expect(caughtMessage).toMatchObject({ + log: { 0: "info" }, + warn: { 0: "warning" }, + error: { 0: "error" }, + }); + }); +}); diff --git a/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts b/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts new file mode 100644 index 00000000..79572fcc --- /dev/null +++ b/jslib/node/spec/services/nodeCryptoFunction.service.spec.ts @@ -0,0 +1,518 @@ +import { Utils } from "jslib-common/misc/utils"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; +import { NodeCryptoFunctionService } from "jslib-node/services/nodeCryptoFunction.service"; + +const RsaPublicKey = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + + "RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN" + + "084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc" + + "xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB"; +const RsaPrivateKey = + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz" + + "YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L" + + "nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/" + + "YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK" + + "PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q" + + "Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj" + + "WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh" + + "5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk" + + "1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU" + + "BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf" + + "TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU" + + "q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv" + + "q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX" + + "5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1" + + "eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE" + + "Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8" + + "+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ" + + "BokBGnjFnTnKcs7nv/O8="; + +const Sha1Mac = "4d4c223f95dc577b665ec4ccbcb680b80a397038"; +const Sha256Mac = "6be3caa84922e12aaaaa2f16c40d44433bb081ef323db584eb616333ab4e874f"; +const Sha512Mac = + "21910e341fa12106ca35758a2285374509326c9fbe0bd64e7b99c898f841dc948c58ce66d3504d8883c" + + "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; + +describe("NodeCrypto Function Service", () => { + describe("pbkdf2", () => { + const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; + const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; + const unicode256Key = "ZdeOata6xoRpB4DLp8zHhXz5kLmkWtX5pd+TdRH8w8w="; + + const regular512Key = + "liTi/Ke8LPU1Qv+Vl7NGEVt/XMbsBVJ2kQxtVG/Z1/JFHFKQW3ZkI81qVlwTiCpb+cFXzs+57" + + "eyhhx5wfKo5Cg=="; + const utf8512Key = + "df0KdvIBeCzD/kyXptwQohaqUa4e7IyFUyhFQjXCANu5T+scq55hCcE4dG4T/MhAk2exw8j7ixRN" + + "zXANiVZpnw=="; + const unicode512Key = + "FE+AnUJaxv8jh+zUDtZz4mjjcYk0/PZDZm+SLJe3XtxtnpdqqpblX6JjuMZt/dYYNMOrb2+mD" + + "L3FiQDTROh1lg=="; + + testPbkdf2("sha256", regular256Key, utf8256Key, unicode256Key); + testPbkdf2("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdf", () => { + const regular256Key = "qBUmEYtwTwwGPuw/z6bs/qYXXYNUlocFlyAuuANI8Pw="; + const utf8256Key = "6DfJwW1R3txgiZKkIFTvVAb7qVlG7lKcmJGJoxR2GBU="; + const unicode256Key = "gejGI82xthA+nKtKmIh82kjw+ttHr+ODsUoGdu5sf0A="; + + const regular512Key = "xe5cIG6ZfwGmb1FvsOedM0XKOm21myZkjL/eDeKIqqM="; + const utf8512Key = "XQMVBnxVEhlvjSFDQc77j5GDE9aorvbS0vKnjhRg0LY="; + const unicode512Key = "148GImrTbrjaGAe/iWEpclINM8Ehhko+9lB14+52lqc="; + + testHkdf("sha256", regular256Key, utf8256Key, unicode256Key); + testHkdf("sha512", regular512Key, utf8512Key, unicode512Key); + }); + + describe("hkdfExpand", () => { + const prk16Byte = "criAmKtfzxanbgea5/kelQ=="; + const prk32Byte = "F5h4KdYQnIVH4rKH0P9CZb1GrR4n16/sJrS0PsQEn0Y="; + const prk64Byte = + "ssBK0mRG17VHdtsgt8yo4v25CRNpauH+0r2fwY/E9rLyaFBAOMbIeTry+" + + "gUJ28p8y+hFh3EI9pcrEWaNvFYonQ=="; + + testHkdfExpand("sha256", prk32Byte, 32, "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD8="); + testHkdfExpand( + "sha256", + prk32Byte, + 64, + "BnIqJlfnHm0e/2iB/15cbHyR19ARPIcWRp4oNS22CD9BV+" + + "/queOZenPNkDhmlVyL2WZ3OSU5+7ISNF5NhNfvZA==" + ); + testHkdfExpand("sha512", prk64Byte, 32, "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlk="); + testHkdfExpand( + "sha512", + prk64Byte, + 64, + "uLWbMWodSBms5uGJ5WTRTesyW+MD7nlpCZvagvIRXlkY5Pv0sB+" + + "MqvaopmkC6sD/j89zDwTV9Ib2fpucUydO8w==" + ); + + it("should fail with prk too small", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk16Byte), + "info", + 32, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("prk is too small.")); + }); + + it("should fail with outputByteSize is too large", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const f = cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(prk32Byte), + "info", + 8161, + "sha256" + ); + await expect(f).rejects.toEqual(new Error("outputByteSize is too large.")); + }); + }); + + describe("hash", () => { + const regular1Hash = "2a241604fb921fad12bf877282457268e1dccb70"; + const utf81Hash = "85672798dc5831e96d6c48655d3d39365a9c88b6"; + const unicode1Hash = "39c975935054a3efc805a9709b60763a823a6ad4"; + + const regular256Hash = "2b8e96031d352a8655d733d7a930b5ffbea69dc25cf65c7bca7dd946278908b2"; + const utf8256Hash = "25fe8440f5b01ed113b0a0e38e721b126d2f3f77a67518c4a04fcde4e33eeb9d"; + const unicode256Hash = "adc1c0c2afd6e92cefdf703f9b6eb2c38e0d6d1a040c83f8505c561fea58852e"; + + const regular512Hash = + "c15cf11d43bde333647e3f559ec4193bb2edeaa0e8b902772f514cdf3f785a3f49a6e02a4b87b3" + + "b47523271ad45b7e0aebb5cdcc1bc54815d256eb5dcb80da9d"; + const utf8512Hash = + "035c31a877a291af09ed2d3a1a293e69c3e079ea2cecc00211f35e6bce10474ca3ad6e30b59e26118" + + "37463f20969c5bc95282965a051a88f8cdf2e166549fcdd"; + const unicode512Hash = + "2b16a5561af8ad6fe414cc103fc8036492e1fc6d9aabe1b655497054f760fe0e34c5d100ac773d" + + "9f3030438284f22dbfa20cb2e9b019f2c98dfe38ce1ef41bae"; + + const regularMd5 = "5eceffa53a5fd58c44134211e2c5f522"; + const utf8Md5 = "3abc9433c09551b939c80aa0aa3174e1"; + const unicodeMd5 = "85ae134072c8d81257933f7045ba17ca"; + + testHash("sha1", regular1Hash, utf81Hash, unicode1Hash); + testHash("sha256", regular256Hash, utf8256Hash, unicode256Hash); + testHash("sha512", regular512Hash, utf8512Hash, unicode512Hash); + testHash("md5", regularMd5, utf8Md5, unicodeMd5); + }); + + describe("hmac", () => { + testHmac("sha1", Sha1Mac); + testHmac("sha256", Sha256Mac); + testHmac("sha512", Sha512Mac); + }); + + describe("compare", () => { + testCompare(false); + }); + + describe("hmacFast", () => { + testHmac("sha1", Sha1Mac, true); + testHmac("sha256", Sha256Mac, true); + testHmac("sha512", Sha512Mac, true); + }); + + describe("compareFast", () => { + testCompare(true); + }); + + describe("aesEncrypt", () => { + it("should successfully encrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromUtf8ToArray("EncryptMe!"); + const encValue = await nodeCryptoFunctionService.aesEncrypt( + data.buffer, + iv.buffer, + key.buffer + ); + expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); + }); + + it("should successfully encrypt and then decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.aesEncrypt( + data.buffer, + iv.buffer, + key.buffer + ); + const decValue = await nodeCryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("aesDecryptFast", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = "ByUF8vhyX4ddU9gcooznwA=="; + const params = nodeCryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await nodeCryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe("EncryptMe!"); + }); + }); + + describe("aesDecrypt", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); + const decValue = await nodeCryptoFunctionService.aesDecrypt( + data.buffer, + iv.buffer, + key.buffer + ); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaEncrypt", () => { + it("should successfully encrypt and then decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = "EncryptMe!"; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.rsaEncrypt( + data.buffer, + pubKey.buffer, + "sha1" + ); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe("rsaDecrypt", () => { + it("should successfully decrypt data", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray( + "A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV" + + "4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT" + + "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" + ); + const decValue = await nodeCryptoFunctionService.rsaDecrypt( + data.buffer, + privKey.buffer, + "sha1" + ); + expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); + }); + }); + + describe("rsaExtractPublicKey", () => { + it("should successfully extract key", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await nodeCryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + + describe("rsaGenerateKeyPair", () => { + testRsaGenerateKeyPair(1024); + testRsaGenerateKeyPair(2048); + + // Generating 4096 bit keys is really slow with Forge lib. + // Maybe move to something else if we ever want to generate keys of this size. + // testRsaGenerateKeyPair(4096); + }); + + describe("randomBytes", () => { + it("should make a value of the correct length", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const randomData = await nodeCryptoFunctionService.randomBytes(16); + expect(randomData.byteLength).toBe(16); + }); + + it("should not make the same value twice", async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const randomData = await nodeCryptoFunctionService.randomBytes(16); + const randomData2 = await nodeCryptoFunctionService.randomBytes(16); + expect( + randomData.byteLength === randomData2.byteLength && randomData !== randomData2 + ).toBeTruthy(); + }); + }); +}); + +function testPbkdf2( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const regularEmail = "user@example.com"; + const utf8Email = "üser@example.com"; + + const regularPassword = "password"; + const utf8Password = "pǻssword"; + const unicodePassword = "😀password🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(regularPassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(utf8Password, utf8Email, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2(unicodePassword, regularEmail, algorithm, 5000); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.pbkdf2( + Utils.fromUtf8ToArray(regularPassword).buffer, + Utils.fromUtf8ToArray(regularEmail).buffer, + algorithm, + 5000 + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdf( + algorithm: "sha256" | "sha512", + regularKey: string, + utf8Key: string, + unicodeKey: string +) { + const ikm = Utils.fromB64ToArray("criAmKtfzxanbgea5/kelQ=="); + + const regularSalt = "salt"; + const utf8Salt = "üser_salt"; + const unicodeSalt = "😀salt🙏"; + + const regularInfo = "info"; + const utf8Info = "üser_info"; + const unicodeInfo = "😀info🙏"; + + it("should create valid " + algorithm + " key from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, regularSalt, regularInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); + + it("should create valid " + algorithm + " key from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, utf8Salt, utf8Info, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(utf8Key); + }); + + it("should create valid " + algorithm + " key from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf(ikm, unicodeSalt, unicodeInfo, 32, algorithm); + expect(Utils.fromBufferToB64(key)).toBe(unicodeKey); + }); + + it("should create valid " + algorithm + " key from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const key = await cryptoFunctionService.hkdf( + ikm, + Utils.fromUtf8ToArray(regularSalt).buffer, + Utils.fromUtf8ToArray(regularInfo).buffer, + 32, + algorithm + ); + expect(Utils.fromBufferToB64(key)).toBe(regularKey); + }); +} + +function testHkdfExpand( + algorithm: "sha256" | "sha512", + b64prk: string, + outputByteSize: number, + b64ExpectedOkm: string +) { + const info = "info"; + + it("should create valid " + algorithm + " " + outputByteSize + " byte okm", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const okm = await cryptoFunctionService.hkdfExpand( + Utils.fromB64ToArray(b64prk), + info, + outputByteSize, + algorithm + ); + expect(Utils.fromBufferToB64(okm)).toBe(b64ExpectedOkm); + }); +} + +function testHash( + algorithm: "sha1" | "sha256" | "sha512" | "md5", + regularHash: string, + utf8Hash: string, + unicodeHash: string +) { + const regularValue = "HashMe!!"; + const utf8Value = "HǻshMe!!"; + const unicodeValue = "😀HashMe!!!🙏"; + + it("should create valid " + algorithm + " hash from regular input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(regularValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); + + it("should create valid " + algorithm + " hash from utf8 input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(utf8Value, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(utf8Hash); + }); + + it("should create valid " + algorithm + " hash from unicode input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash(unicodeValue, algorithm); + expect(Utils.fromBufferToHex(hash)).toBe(unicodeHash); + }); + + it("should create valid " + algorithm + " hash from array buffer input", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const hash = await cryptoFunctionService.hash( + Utils.fromUtf8ToArray(regularValue).buffer, + algorithm + ); + expect(Utils.fromBufferToHex(hash)).toBe(regularHash); + }); +} + +function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string, fast = false) { + it("should create valid " + algorithm + " hmac", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const value = Utils.fromUtf8ToArray("SignMe!!").buffer; + const key = Utils.fromUtf8ToArray("secretkey").buffer; + let computedMac: ArrayBuffer = null; + if (fast) { + computedMac = await cryptoFunctionService.hmacFast(value, key, algorithm); + } else { + computedMac = await cryptoFunctionService.hmac(value, key, algorithm); + } + expect(Utils.fromBufferToHex(computedMac)).toBe(mac); + }); +} + +function testCompare(fast = false) { + it("should successfully compare two of the same values", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, a.buffer) + : await cryptoFunctionService.compare(a.buffer, a.buffer); + expect(equal).toBe(true); + }); + + it("should successfully compare two different values of the same length", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + b[1] = 4; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) + : await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); + + it("should successfully compare two different values of different lengths", async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const a = new Uint8Array(2); + a[0] = 1; + a[1] = 2; + const b = new Uint8Array(2); + b[0] = 3; + const equal = fast + ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) + : await cryptoFunctionService.compare(a.buffer, b.buffer); + expect(equal).toBe(false); + }); +} + +function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { + it( + "should successfully generate a " + length + " bit key pair", + async () => { + const cryptoFunctionService = new NodeCryptoFunctionService(); + const keyPair = await cryptoFunctionService.rsaGenerateKeyPair(length); + expect(keyPair[0] == null || keyPair[1] == null).toBe(false); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]); + expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey)); + }, + 30000 + ); +} + +function makeStaticByteArray(length: number) { + const arr = new Uint8Array(length); + for (let i = 0; i < length; i++) { + arr[i] = i; + } + return arr; +} diff --git a/jslib/node/spec/test.ts b/jslib/node/spec/test.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/node/src/cli/baseProgram.ts b/jslib/node/src/cli/baseProgram.ts new file mode 100644 index 00000000..4a2ec904 --- /dev/null +++ b/jslib/node/src/cli/baseProgram.ts @@ -0,0 +1,113 @@ +import * as chalk from "chalk"; + +import { StateService } from "jslib-common/abstractions/state.service"; + +import { Response } from "./models/response"; +import { ListResponse } from "./models/response/listResponse"; +import { MessageResponse } from "./models/response/messageResponse"; +import { StringResponse } from "./models/response/stringResponse"; + +export abstract class BaseProgram { + constructor( + protected stateService: StateService, + private writeLn: (s: string, finalLine: boolean, error: boolean) => void + ) {} + + protected processResponse( + response: Response, + exitImmediately = false, + dataProcessor: () => string = null + ) { + if (!response.success) { + if (process.env.BW_QUIET !== "true") { + if (process.env.BW_RESPONSE === "true") { + this.writeLn(this.getJson(response), true, false); + } else { + this.writeLn(chalk.redBright(response.message), true, true); + } + } + const exitCode = process.env.BW_CLEANEXIT ? 0 : 1; + if (exitImmediately) { + process.exit(exitCode); + } else { + process.exitCode = exitCode; + } + return; + } + + if (process.env.BW_RESPONSE === "true") { + this.writeLn(this.getJson(response), true, false); + } else if (response.data != null) { + let out: string = dataProcessor != null ? dataProcessor() : null; + if (out == null) { + if (response.data.object === "string") { + const data = (response.data as StringResponse).data; + if (data != null) { + out = data; + } + } else if (response.data.object === "list") { + out = this.getJson((response.data as ListResponse).data); + } else if (response.data.object === "message") { + out = this.getMessage(response); + } else { + out = this.getJson(response.data); + } + } + + if (out != null && process.env.BW_QUIET !== "true") { + this.writeLn(out, true, false); + } + } + if (exitImmediately) { + process.exit(0); + } else { + process.exitCode = 0; + } + } + + protected getJson(obj: any): string { + if (process.env.BW_PRETTY === "true") { + return JSON.stringify(obj, null, " "); + } else { + return JSON.stringify(obj); + } + } + + protected getMessage(response: Response): string { + const message = response.data as MessageResponse; + if (process.env.BW_RAW === "true") { + return message.raw; + } + + let out = ""; + if (message.title != null) { + if (message.noColor) { + out = message.title; + } else { + out = chalk.greenBright(message.title); + } + } + if (message.message != null) { + if (message.title != null) { + out += "\n"; + } + out += message.message; + } + return out.trim() === "" ? null : out; + } + + protected async exitIfAuthed() { + const authed = await this.stateService.getIsAuthenticated(); + if (authed) { + const email = await this.stateService.getEmail(); + this.processResponse(Response.error("You are already logged in as " + email + "."), true); + } + } + + protected async exitIfNotAuthed() { + const authed = await this.stateService.getIsAuthenticated(); + if (!authed) { + this.processResponse(Response.error("You are not logged in."), true); + } + } +} diff --git a/jslib/node/src/cli/commands/login.command.ts b/jslib/node/src/cli/commands/login.command.ts new file mode 100644 index 00000000..ee6f8228 --- /dev/null +++ b/jslib/node/src/cli/commands/login.command.ts @@ -0,0 +1,624 @@ +import * as http from "http"; + +import * as program from "commander"; +import * as inquirer from "inquirer"; +import Separator from "inquirer/lib/objects/separator"; + +import { ApiService } from "jslib-common/abstractions/api.service"; +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; +import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; +import { Utils } from "jslib-common/misc/utils"; +import { AuthResult } from "jslib-common/models/domain/authResult"; +import { + ApiLogInCredentials, + PasswordLogInCredentials, + SsoLogInCredentials, +} from "jslib-common/models/domain/logInCredentials"; +import { TokenRequestTwoFactor } from "jslib-common/models/request/identityToken/tokenRequestTwoFactor"; +import { TwoFactorEmailRequest } from "jslib-common/models/request/twoFactorEmailRequest"; +import { UpdateTempPasswordRequest } from "jslib-common/models/request/updateTempPasswordRequest"; +import { ErrorResponse } from "jslib-common/models/response/errorResponse"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class LoginCommand { + protected validatedParams: () => Promise; + protected success: () => Promise; + protected logout: () => Promise; + protected canInteract: boolean; + protected clientId: string; + protected clientSecret: string; + protected email: string; + + private ssoRedirectUri: string = null; + + constructor( + protected authService: AuthService, + protected apiService: ApiService, + protected i18nService: I18nService, + protected environmentService: EnvironmentService, + protected passwordGenerationService: PasswordGenerationService, + protected cryptoFunctionService: CryptoFunctionService, + protected platformUtilsService: PlatformUtilsService, + protected stateService: StateService, + protected cryptoService: CryptoService, + protected policyService: PolicyService, + protected twoFactorService: TwoFactorService, + clientId: string + ) { + this.clientId = clientId; + } + + async run(email: string, password: string, options: program.OptionValues) { + this.canInteract = process.env.BW_NOINTERACTION !== "true"; + + let ssoCodeVerifier: string = null; + let ssoCode: string = null; + let orgIdentifier: string = null; + + let clientId: string = null; + let clientSecret: string = null; + + let selectedProvider: any = null; + + if (options.apikey != null) { + const apiIdentifiers = await this.apiIdentifiers(); + clientId = apiIdentifiers.clientId; + clientSecret = apiIdentifiers.clientSecret; + } else if (options.sso != null && this.canInteract) { + const passwordOptions: any = { + type: "password", + length: 64, + uppercase: true, + lowercase: true, + numbers: true, + special: false, + }; + const state = await this.passwordGenerationService.generatePassword(passwordOptions); + ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); + const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256"); + const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + try { + const ssoParams = await this.openSsoPrompt(codeChallenge, state); + ssoCode = ssoParams.ssoCode; + orgIdentifier = ssoParams.orgIdentifier; + } catch { + return Response.badRequest("Something went wrong. Try again."); + } + } else { + if ((email == null || email === "") && this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "email", + message: "Email address:", + }); + email = answer.email; + } + if (email == null || email.trim() === "") { + return Response.badRequest("Email address is required."); + } + if (email.indexOf("@") === -1) { + return Response.badRequest("Email address is invalid."); + } + this.email = email; + + if (password == null || password === "") { + if (options.passwordfile) { + password = await NodeUtils.readFirstLine(options.passwordfile); + } else if (options.passwordenv && process.env[options.passwordenv]) { + password = process.env[options.passwordenv]; + } else if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "password", + name: "password", + message: "Master password:", + }); + password = answer.password; + } + } + + if (password == null || password === "") { + return Response.badRequest("Master password is required."); + } + } + + let twoFactorToken: string = options.code; + let twoFactorMethod: TwoFactorProviderType = null; + try { + if (options.method != null) { + twoFactorMethod = parseInt(options.method, null); + } + } catch (e) { + return Response.error("Invalid two-step login method."); + } + + const twoFactor = + twoFactorToken == null + ? null + : new TokenRequestTwoFactor(twoFactorMethod, twoFactorToken, false); + + try { + if (this.validatedParams != null) { + await this.validatedParams(); + } + + let response: AuthResult = null; + if (clientId != null && clientSecret != null) { + response = await this.authService.logIn(new ApiLogInCredentials(clientId, clientSecret)); + } else if (ssoCode != null && ssoCodeVerifier != null) { + response = await this.authService.logIn( + new SsoLogInCredentials( + ssoCode, + ssoCodeVerifier, + this.ssoRedirectUri, + orgIdentifier, + twoFactor + ) + ); + } else { + response = await this.authService.logIn( + new PasswordLogInCredentials(email, password, null, twoFactor) + ); + } + if (response.captchaSiteKey) { + const credentials = new PasswordLogInCredentials(email, password); + const handledResponse = await this.handleCaptchaRequired(twoFactor, credentials); + + // Error Response + if (handledResponse instanceof Response) { + return handledResponse; + } else { + response = handledResponse; + } + } + if (response.requiresTwoFactor) { + const twoFactorProviders = this.twoFactorService.getSupportedProviders(null); + if (twoFactorProviders.length === 0) { + return Response.badRequest("No providers available for this client."); + } + + if (twoFactorMethod != null) { + try { + selectedProvider = twoFactorProviders.filter((p) => p.type === twoFactorMethod)[0]; + } catch (e) { + return Response.error("Invalid two-step login method."); + } + } + + if (selectedProvider == null) { + if (twoFactorProviders.length === 1) { + selectedProvider = twoFactorProviders[0]; + } else if (this.canInteract) { + const twoFactorOptions: (string | Separator)[] = twoFactorProviders.map((p) => p.name); + twoFactorOptions.push(new inquirer.Separator()); + twoFactorOptions.push("Cancel"); + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "list", + name: "method", + message: "Two-step login method:", + choices: twoFactorOptions, + }); + const i = twoFactorOptions.indexOf(answer.method); + if (i === twoFactorOptions.length - 1) { + return Response.error("Login failed."); + } + selectedProvider = twoFactorProviders[i]; + } + if (selectedProvider == null) { + return Response.error("Login failed. No provider selected."); + } + } + + if ( + twoFactorToken == null && + response.twoFactorProviders.size > 1 && + selectedProvider.type === TwoFactorProviderType.Email + ) { + const emailReq = new TwoFactorEmailRequest(); + emailReq.email = this.authService.email; + emailReq.masterPasswordHash = this.authService.masterPasswordHash; + await this.apiService.postTwoFactorEmail(emailReq); + } + + if (twoFactorToken == null) { + if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "token", + message: "Two-step login code:", + }); + twoFactorToken = answer.token; + } + if (twoFactorToken == null || twoFactorToken === "") { + return Response.badRequest("Code is required."); + } + } + + response = await this.authService.logInTwoFactor( + new TokenRequestTwoFactor(selectedProvider.type, twoFactorToken), + null + ); + } + + if (response.captchaSiteKey) { + const twoFactorRequest = new TokenRequestTwoFactor(selectedProvider.type, twoFactorToken); + const handledResponse = await this.handleCaptchaRequired(twoFactorRequest); + + // Error Response + if (handledResponse instanceof Response) { + return handledResponse; + } else { + response = handledResponse; + } + } + + if (response.requiresTwoFactor) { + return Response.error("Login failed."); + } + + if (response.resetMasterPassword) { + return Response.error( + "In order to log in with SSO from the CLI, you must first log in" + + " through the web vault to set your master password." + ); + } + + // Handle Updating Temp Password if NOT using an API Key for authentication + if (response.forcePasswordReset && clientId == null && clientSecret == null) { + return await this.updateTempPassword(); + } + + return await this.handleSuccessResponse(); + } catch (e) { + return Response.error(e); + } + } + + private async handleSuccessResponse(): Promise { + if (this.success != null) { + const res = await this.success(); + return Response.success(res); + } else { + const res = new MessageResponse("You are logged in!", null); + return Response.success(res); + } + } + + private async updateTempPassword(error?: string): Promise { + // If no interaction available, alert user to use web vault + if (!this.canInteract) { + await this.logout(); + this.authService.logOut(() => { + /* Do nothing */ + }); + return Response.error( + new MessageResponse( + "An organization administrator recently changed your master password. In order to access the vault, you must update your master password now via the web vault. You have been logged out.", + null + ) + ); + } + + if (this.email == null || this.email === "undefined") { + this.email = await this.stateService.getEmail(); + } + + // Get New Master Password + const baseMessage = + "An organization administrator recently changed your master password. In order to access the vault, you must update your master password now.\n" + + "Master password: "; + const firstMessage = error != null ? error + baseMessage : baseMessage; + const mp: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "password", + name: "password", + message: firstMessage, + }); + const masterPassword = mp.password; + + // Master Password Validation + if (masterPassword == null || masterPassword === "") { + return this.updateTempPassword("Master password is required.\n"); + } + + if (masterPassword.length < 8) { + return this.updateTempPassword("Master password must be at least 8 characters long.\n"); + } + + // Strength & Policy Validation + const strengthResult = this.passwordGenerationService.passwordStrength( + masterPassword, + this.getPasswordStrengthUserInput() + ); + + // Get New Master Password Re-type + const reTypeMessage = "Re-type New Master password (Strength: " + strengthResult.score + ")"; + const retype: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "password", + name: "password", + message: reTypeMessage, + }); + const masterPasswordRetype = retype.password; + + // Re-type Validation + if (masterPassword !== masterPasswordRetype) { + return this.updateTempPassword("Master password confirmation does not match.\n"); + } + + // Get Hint (optional) + const hint: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ + type: "input", + name: "input", + message: "Master Password Hint (optional):", + }); + const masterPasswordHint = hint.input; + + // Retrieve details for key generation + const enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions(); + const kdf = await this.stateService.getKdfType(); + const kdfIterations = await this.stateService.getKdfIterations(); + + if ( + enforcedPolicyOptions != null && + !this.policyService.evaluateMasterPassword( + strengthResult.score, + masterPassword, + enforcedPolicyOptions + ) + ) { + return this.updateTempPassword( + "Your new master password does not meet the policy requirements.\n" + ); + } + + try { + // Create new key and hash new password + const newKey = await this.cryptoService.makeKey( + masterPassword, + this.email.trim().toLowerCase(), + kdf, + kdfIterations + ); + const newPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey); + + // Grab user's current enc key + const userEncKey = await this.cryptoService.getEncKey(); + + // Create new encKey for the User + const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey); + + // Create request + const request = new UpdateTempPasswordRequest(); + request.key = newEncKey[1].encryptedString; + request.newMasterPasswordHash = newPasswordHash; + request.masterPasswordHint = masterPasswordHint; + + // Update user's password + await this.apiService.putUpdateTempPassword(request); + return this.handleSuccessResponse(); + } catch (e) { + await this.logout(); + this.authService.logOut(() => { + /* Do nothing */ + }); + return Response.error(e); + } + } + + private async handleCaptchaRequired( + twoFactorRequest: TokenRequestTwoFactor, + credentials: PasswordLogInCredentials = null + ): Promise { + const badCaptcha = Response.badRequest( + "Your authentication request has been flagged and will require user interaction to proceed.\n" + + "Please use your API key to validate this request and ensure BW_CLIENTSECRET is correct, if set.\n" + + "(https://bitwarden.com/help/cli-auth-challenges)" + ); + + try { + const captchaClientSecret = await this.apiClientSecret(true); + if (Utils.isNullOrWhitespace(captchaClientSecret)) { + return badCaptcha; + } + + let authResultResponse: AuthResult = null; + if (credentials != null) { + credentials.captchaToken = captchaClientSecret; + credentials.twoFactor = twoFactorRequest; + authResultResponse = await this.authService.logIn(credentials); + } else { + authResultResponse = await this.authService.logInTwoFactor( + twoFactorRequest, + captchaClientSecret + ); + } + + return authResultResponse; + } catch (e) { + if ( + e instanceof ErrorResponse || + (e.constructor.name === ErrorResponse.name && + (e as ErrorResponse).message.includes("Captcha is invalid")) + ) { + return badCaptcha; + } else { + return Response.error(e); + } + } + } + + private getPasswordStrengthUserInput() { + let userInput: string[] = []; + const atPosition = this.email.indexOf("@"); + if (atPosition > -1) { + userInput = userInput.concat( + this.email + .substr(0, atPosition) + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/) + ); + } + return userInput; + } + + private async apiClientId(): Promise { + let clientId: string = null; + + const storedClientId: string = process.env.BW_CLIENTID; + if (storedClientId == null) { + if (this.canInteract) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "clientId", + message: "client_id:", + }); + clientId = answer.clientId; + } else { + clientId = null; + } + } else { + clientId = storedClientId; + } + + return clientId; + } + + private async apiClientSecret(isAdditionalAuthentication = false): Promise { + const additionalAuthenticationMessage = "Additional authentication required.\nAPI key "; + let clientSecret: string = null; + + const storedClientSecret: string = this.clientSecret || process.env.BW_CLIENTSECRET; + if (this.canInteract && storedClientSecret == null) { + const answer: inquirer.Answers = await inquirer.createPromptModule({ + output: process.stderr, + })({ + type: "input", + name: "clientSecret", + message: + (isAdditionalAuthentication ? additionalAuthenticationMessage : "") + "client_secret:", + }); + clientSecret = answer.clientSecret; + } else { + clientSecret = storedClientSecret; + } + + return clientSecret; + } + + private async apiIdentifiers(): Promise<{ clientId: string; clientSecret: string }> { + return { + clientId: await this.apiClientId(), + clientSecret: await this.apiClientSecret(), + }; + } + + private async openSsoPrompt( + codeChallenge: string, + state: string + ): Promise<{ ssoCode: string; orgIdentifier: string }> { + return new Promise((resolve, reject) => { + const callbackServer = http.createServer((req, res) => { + const urlString = "http://localhost" + req.url; + const url = new URL(urlString); + const code = url.searchParams.get("code"); + const receivedState = url.searchParams.get("state"); + const orgIdentifier = this.getOrgIdentifierFromState(receivedState); + res.setHeader("Content-Type", "text/html"); + if (code != null && receivedState != null && this.checkState(receivedState, state)) { + res.writeHead(200); + res.end( + "Success | Bitwarden CLI" + + "

Successfully authenticated with the Bitwarden CLI

" + + "

You may now close this tab and return to the terminal.

" + + "" + ); + callbackServer.close(() => + resolve({ + ssoCode: code, + orgIdentifier: orgIdentifier, + }) + ); + } else { + res.writeHead(400); + res.end( + "Failed | Bitwarden CLI" + + "

Something went wrong logging into the Bitwarden CLI

" + + "

You may now close this tab and return to the terminal.

" + + "" + ); + callbackServer.close(() => reject()); + } + }); + let foundPort = false; + const webUrl = this.environmentService.getWebVaultUrl(); + for (let port = 8065; port <= 8070; port++) { + try { + this.ssoRedirectUri = "http://localhost:" + port; + callbackServer.listen(port, () => { + this.platformUtilsService.launchUri( + webUrl + + "/#/sso?clientId=" + + this.clientId + + "&redirectUri=" + + encodeURIComponent(this.ssoRedirectUri) + + "&state=" + + state + + "&codeChallenge=" + + codeChallenge + ); + }); + foundPort = true; + break; + } catch { + // Ignore error since we run the same command up to 5 times. + } + } + if (!foundPort) { + reject(); + } + }); + } + + private getOrgIdentifierFromState(state: string): string { + if (state === null || state === undefined) { + return null; + } + + const stateSplit = state.split("_identifier="); + return stateSplit.length > 1 ? stateSplit[1] : null; + } + + private checkState(state: string, checkState: string): boolean { + if (state === null || state === undefined) { + return false; + } + if (checkState === null || checkState === undefined) { + return false; + } + + const stateSplit = state.split("_identifier="); + const checkStateSplit = checkState.split("_identifier="); + return stateSplit[0] === checkStateSplit[0]; + } +} diff --git a/jslib/node/src/cli/commands/logout.command.ts b/jslib/node/src/cli/commands/logout.command.ts new file mode 100644 index 00000000..6074ba9c --- /dev/null +++ b/jslib/node/src/cli/commands/logout.command.ts @@ -0,0 +1,22 @@ +import { AuthService } from "jslib-common/abstractions/auth.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class LogoutCommand { + constructor( + private authService: AuthService, + private i18nService: I18nService, + private logoutCallback: () => Promise + ) {} + + async run() { + await this.logoutCallback(); + this.authService.logOut(() => { + /* Do nothing */ + }); + const res = new MessageResponse("You have logged out.", null); + return Response.success(res); + } +} diff --git a/jslib/node/src/cli/commands/update.command.ts b/jslib/node/src/cli/commands/update.command.ts new file mode 100644 index 00000000..1b9408a3 --- /dev/null +++ b/jslib/node/src/cli/commands/update.command.ts @@ -0,0 +1,104 @@ +import * as fetch from "node-fetch"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; + +import { Response } from "../models/response"; +import { MessageResponse } from "../models/response/messageResponse"; + +export class UpdateCommand { + inPkg = false; + + constructor( + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private repoName: string, + private executableName: string, + private showExtendedMessage: boolean + ) { + this.inPkg = !!(process as any).pkg; + } + + async run(): Promise { + const currentVersion = await this.platformUtilsService.getApplicationVersion(); + + const response = await fetch.default( + "https://api.github.com/repos/bitwarden/" + this.repoName + "/releases/latest" + ); + if (response.status === 200) { + const responseJson = await response.json(); + const res = new MessageResponse(null, null); + + const tagName: string = responseJson.tag_name; + if (tagName === "v" + currentVersion) { + res.title = "No update available."; + res.noColor = true; + return Response.success(res); + } + + let downloadUrl: string = null; + if (responseJson.assets != null) { + for (const a of responseJson.assets) { + const download: string = a.browser_download_url; + if (download == null) { + continue; + } + + if (download.indexOf(".zip") === -1) { + continue; + } + + if ( + process.platform === "win32" && + download.indexOf(this.executableName + "-windows") > -1 + ) { + downloadUrl = download; + break; + } else if ( + process.platform === "darwin" && + download.indexOf(this.executableName + "-macos") > -1 + ) { + downloadUrl = download; + break; + } else if ( + process.platform === "linux" && + download.indexOf(this.executableName + "-linux") > -1 + ) { + downloadUrl = download; + break; + } + } + } + + res.title = "A new version is available: " + tagName; + if (downloadUrl == null) { + downloadUrl = "https://github.com/bitwarden/" + this.repoName + "/releases"; + } else { + res.raw = downloadUrl; + } + res.message = ""; + if (responseJson.body != null && responseJson.body !== "") { + res.message = responseJson.body + "\n\n"; + } + + res.message += "You can download this update at " + downloadUrl; + + if (this.showExtendedMessage) { + if (this.inPkg) { + res.message += + "\n\nIf you installed this CLI through a package manager " + + "you should probably update using its update command instead."; + } else { + res.message += + "\n\nIf you installed this CLI through NPM " + + "you should update using `npm install -g @bitwarden/" + + this.repoName + + "`"; + } + } + return Response.success(res); + } else { + return Response.error("Error contacting update API: " + response.status); + } + } +} diff --git a/jslib/node/src/cli/models/response.ts b/jslib/node/src/cli/models/response.ts new file mode 100644 index 00000000..d768c51f --- /dev/null +++ b/jslib/node/src/cli/models/response.ts @@ -0,0 +1,50 @@ +import { BaseResponse } from "./response/baseResponse"; + +export class Response { + static error(error: any, data?: any): Response { + const res = new Response(); + res.success = false; + if (typeof error === "string") { + res.message = error; + } else { + res.message = + error.message != null + ? error.message + : error.toString() === "[object Object]" + ? JSON.stringify(error) + : error.toString(); + } + res.data = data; + return res; + } + + static notFound(): Response { + return Response.error("Not found."); + } + + static badRequest(message: string): Response { + return Response.error(message); + } + + static multipleResults(ids: string[]): Response { + let msg = + "More than one result was found. Try getting a specific object by `id` instead. " + + "The following objects were found:"; + ids.forEach((id) => { + msg += "\n" + id; + }); + return Response.error(msg, ids); + } + + static success(data?: BaseResponse): Response { + const res = new Response(); + res.success = true; + res.data = data; + return res; + } + + success: boolean; + message: string; + errorCode: number; + data: BaseResponse; +} diff --git a/jslib/node/src/cli/models/response/baseResponse.ts b/jslib/node/src/cli/models/response/baseResponse.ts new file mode 100644 index 00000000..b0cc57da --- /dev/null +++ b/jslib/node/src/cli/models/response/baseResponse.ts @@ -0,0 +1,3 @@ +export interface BaseResponse { + object: string; +} diff --git a/jslib/node/src/cli/models/response/fileResponse.ts b/jslib/node/src/cli/models/response/fileResponse.ts new file mode 100644 index 00000000..c16b56e0 --- /dev/null +++ b/jslib/node/src/cli/models/response/fileResponse.ts @@ -0,0 +1,13 @@ +import { BaseResponse } from "./baseResponse"; + +export class FileResponse implements BaseResponse { + object: string; + data: Buffer; + fileName: string; + + constructor(data: Buffer, fileName: string) { + this.object = "file"; + this.data = data; + this.fileName = fileName; + } +} diff --git a/jslib/node/src/cli/models/response/listResponse.ts b/jslib/node/src/cli/models/response/listResponse.ts new file mode 100644 index 00000000..db415bcd --- /dev/null +++ b/jslib/node/src/cli/models/response/listResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class ListResponse implements BaseResponse { + object: string; + data: BaseResponse[]; + + constructor(data: BaseResponse[]) { + this.object = "list"; + this.data = data; + } +} diff --git a/jslib/node/src/cli/models/response/messageResponse.ts b/jslib/node/src/cli/models/response/messageResponse.ts new file mode 100644 index 00000000..8612841a --- /dev/null +++ b/jslib/node/src/cli/models/response/messageResponse.ts @@ -0,0 +1,15 @@ +import { BaseResponse } from "./baseResponse"; + +export class MessageResponse implements BaseResponse { + object: string; + title: string; + message: string; + raw: string; + noColor = false; + + constructor(title: string, message: string) { + this.object = "message"; + this.title = title; + this.message = message; + } +} diff --git a/jslib/node/src/cli/models/response/stringResponse.ts b/jslib/node/src/cli/models/response/stringResponse.ts new file mode 100644 index 00000000..f4becfe8 --- /dev/null +++ b/jslib/node/src/cli/models/response/stringResponse.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "./baseResponse"; + +export class StringResponse implements BaseResponse { + object: string; + data: string; + + constructor(data: string) { + this.object = "string"; + this.data = data; + } +} diff --git a/jslib/node/src/cli/services/cliPlatformUtils.service.ts b/jslib/node/src/cli/services/cliPlatformUtils.service.ts new file mode 100644 index 00000000..520170c2 --- /dev/null +++ b/jslib/node/src/cli/services/cliPlatformUtils.service.ts @@ -0,0 +1,166 @@ +import * as child_process from "child_process"; + +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { ClientType } from "jslib-common/enums/clientType"; +import { DeviceType } from "jslib-common/enums/deviceType"; +import { ThemeType } from "jslib-common/enums/themeType"; + +// eslint-disable-next-line +const open = require("open"); + +export class CliPlatformUtilsService implements PlatformUtilsService { + clientType: ClientType; + + private deviceCache: DeviceType = null; + + constructor(clientType: ClientType, private packageJson: any) { + this.clientType = clientType; + } + + getDevice(): DeviceType { + if (!this.deviceCache) { + switch (process.platform) { + case "win32": + this.deviceCache = DeviceType.WindowsDesktop; + break; + case "darwin": + this.deviceCache = DeviceType.MacOsDesktop; + break; + case "linux": + default: + this.deviceCache = DeviceType.LinuxDesktop; + break; + } + } + + return this.deviceCache; + } + + getDeviceString(): string { + const device = DeviceType[this.getDevice()].toLowerCase(); + return device.replace("desktop", ""); + } + + getClientType() { + return this.clientType; + } + + isFirefox() { + return false; + } + + isChrome() { + return false; + } + + isEdge() { + return false; + } + + isOpera() { + return false; + } + + isVivaldi() { + return false; + } + + isSafari() { + return false; + } + + isMacAppStore() { + return false; + } + + isViewOpen() { + return Promise.resolve(false); + } + + launchUri(uri: string, options?: any): void { + if (process.platform === "linux") { + child_process.spawnSync("xdg-open", [uri]); + } else { + open(uri); + } + } + + saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void { + throw new Error("Not implemented."); + } + + getApplicationVersion(): Promise { + return Promise.resolve(this.packageJson.version); + } + + getApplicationVersionSync(): string { + return this.packageJson.version; + } + + supportsWebAuthn(win: Window) { + return false; + } + + supportsDuo(): boolean { + return false; + } + + showToast( + type: "error" | "success" | "warning" | "info", + title: string, + text: string | string[], + options?: any + ): void { + throw new Error("Not implemented."); + } + + showDialog( + text: string, + title?: string, + confirmText?: string, + cancelText?: string, + type?: string + ): Promise { + throw new Error("Not implemented."); + } + + isDev(): boolean { + return process.env.BWCLI_ENV === "development"; + } + + isSelfHost(): boolean { + return false; + } + + copyToClipboard(text: string, options?: any): void { + throw new Error("Not implemented."); + } + + readFromClipboard(options?: any): Promise { + throw new Error("Not implemented."); + } + + supportsBiometric(): Promise { + return Promise.resolve(false); + } + + authenticateBiometric(): Promise { + return Promise.resolve(false); + } + + getDefaultSystemTheme() { + return Promise.resolve(ThemeType.Light as ThemeType.Light | ThemeType.Dark); + } + + onDefaultSystemThemeChange() { + /* noop */ + } + + getEffectiveTheme() { + return Promise.resolve(ThemeType.Light); + } + + supportsSecureStorage(): boolean { + return false; + } +} diff --git a/jslib/node/src/cli/services/consoleLog.service.ts b/jslib/node/src/cli/services/consoleLog.service.ts new file mode 100644 index 00000000..73dfcc55 --- /dev/null +++ b/jslib/node/src/cli/services/consoleLog.service.ts @@ -0,0 +1,22 @@ +import { LogLevelType } from "jslib-common/enums/logLevelType"; +import { ConsoleLogService as BaseConsoleLogService } from "jslib-common/services/consoleLog.service"; + +export class ConsoleLogService extends BaseConsoleLogService { + constructor(isDev: boolean, filter: (level: LogLevelType) => boolean = null) { + super(isDev, filter); + } + + write(level: LogLevelType, message: string) { + if (this.filter != null && this.filter(level)) { + return; + } + + if (process.env.BW_RESPONSE === "true") { + // eslint-disable-next-line + console.error(message); + return; + } + + super.write(level, message); + } +} diff --git a/jslib/node/src/globals.d.ts b/jslib/node/src/globals.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/jslib/node/src/services/lowdbStorage.service.ts b/jslib/node/src/services/lowdbStorage.service.ts new file mode 100644 index 00000000..e78fe2e4 --- /dev/null +++ b/jslib/node/src/services/lowdbStorage.service.ts @@ -0,0 +1,148 @@ +import * as fs from "fs"; +import * as path from "path"; + +import * as lowdb from "lowdb"; +import * as FileSync from "lowdb/adapters/FileSync"; + +import { LogService } from "jslib-common/abstractions/log.service"; +import { StorageService } from "jslib-common/abstractions/storage.service"; +import { NodeUtils } from "jslib-common/misc/nodeUtils"; +import { sequentialize } from "jslib-common/misc/sequentialize"; +import { Utils } from "jslib-common/misc/utils"; + +export class LowdbStorageService implements StorageService { + protected dataFilePath: string; + private db: lowdb.LowdbSync; + private defaults: any; + private ready = false; + + constructor( + protected logService: LogService, + defaults?: any, + private dir?: string, + private allowCache = false + ) { + this.defaults = defaults; + } + + @sequentialize(() => "lowdbStorageInit") + async init() { + if (this.ready) { + return; + } + + this.logService.info("Initializing lowdb storage service."); + let adapter: lowdb.AdapterSync; + if (Utils.isNode && this.dir != null) { + if (!fs.existsSync(this.dir)) { + this.logService.warning(`Could not find dir, "${this.dir}"; creating it instead.`); + NodeUtils.mkdirpSync(this.dir, "700"); + this.logService.info(`Created dir "${this.dir}".`); + } + this.dataFilePath = path.join(this.dir, "data.json"); + if (!fs.existsSync(this.dataFilePath)) { + this.logService.warning( + `Could not find data file, "${this.dataFilePath}"; creating it instead.` + ); + fs.writeFileSync(this.dataFilePath, "", { mode: 0o600 }); + fs.chmodSync(this.dataFilePath, 0o600); + this.logService.info(`Created data file "${this.dataFilePath}" with chmod 600.`); + } else { + this.logService.info(`db file "${this.dataFilePath} already exists"; using existing db`); + } + await this.lockDbFile(() => { + adapter = new FileSync(this.dataFilePath); + }); + } + try { + this.logService.info("Attempting to create lowdb storage adapter."); + this.db = lowdb(adapter); + this.logService.info("Successfully created lowdb storage adapter."); + } catch (e) { + if (e instanceof SyntaxError) { + this.logService.warning( + `Error creating lowdb storage adapter, "${e.message}"; emptying data file.` + ); + if (fs.existsSync(this.dataFilePath)) { + const backupPath = this.dataFilePath + ".bak"; + this.logService.warning(`Writing backup of data file to ${backupPath}`); + await fs.copyFile(this.dataFilePath, backupPath, () => { + this.logService.warning( + `Error while creating data file backup, "${e.message}". No backup may have been created.` + ); + }); + } + adapter.write({}); + this.db = lowdb(adapter); + } else { + this.logService.error(`Error creating lowdb storage adapter, "${e.message}".`); + throw e; + } + } + + if (this.defaults != null) { + this.lockDbFile(() => { + this.logService.info("Writing defaults."); + this.readForNoCache(); + this.db.defaults(this.defaults).write(); + this.logService.info("Successfully wrote defaults to db."); + }); + } + + this.ready = true; + } + + async get(key: string): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + const val = this.db.get(key).value(); + this.logService.debug(`Successfully read ${key} from db`); + if (val == null) { + return null; + } + return val as T; + }); + } + + has(key: string): Promise { + return this.get(key).then((v) => v != null); + } + + async save(key: string, obj: any): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + this.db.set(key, obj).write(); + this.logService.debug(`Successfully wrote ${key} to db`); + return; + }); + } + + async remove(key: string): Promise { + await this.waitForReady(); + return this.lockDbFile(() => { + this.readForNoCache(); + this.db.unset(key).write(); + this.logService.debug(`Successfully removed ${key} from db`); + return; + }); + } + + protected async lockDbFile(action: () => T): Promise { + // Lock methods implemented in clients + return Promise.resolve(action()); + } + + private readForNoCache() { + if (!this.allowCache) { + this.db.read(); + } + } + + private async waitForReady() { + if (!this.ready) { + await this.init(); + } + } +} diff --git a/jslib/node/src/services/nodeApi.service.ts b/jslib/node/src/services/nodeApi.service.ts new file mode 100644 index 00000000..7dd03412 --- /dev/null +++ b/jslib/node/src/services/nodeApi.service.ts @@ -0,0 +1,43 @@ +import * as FormData from "form-data"; +import { HttpsProxyAgent } from "https-proxy-agent"; +import * as fe from "node-fetch"; + +import { AppIdService } from "jslib-common/abstractions/appId.service"; +import { EnvironmentService } from "jslib-common/abstractions/environment.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { TokenService } from "jslib-common/abstractions/token.service"; +import { ApiService } from "jslib-common/services/api.service"; + +(global as any).fetch = fe.default; +(global as any).Request = fe.Request; +(global as any).Response = fe.Response; +(global as any).Headers = fe.Headers; +(global as any).FormData = FormData; + +export class NodeApiService extends ApiService { + constructor( + tokenService: TokenService, + platformUtilsService: PlatformUtilsService, + environmentService: EnvironmentService, + appIdService: AppIdService, + logoutCallback: (expired: boolean) => Promise, + customUserAgent: string = null + ) { + super( + tokenService, + platformUtilsService, + environmentService, + appIdService, + logoutCallback, + customUserAgent + ); + } + + nativeFetch(request: Request): Promise { + const proxy = process.env.http_proxy || process.env.https_proxy; + if (proxy) { + (request as any).agent = new HttpsProxyAgent(proxy); + } + return fetch(request); + } +} diff --git a/jslib/node/src/services/nodeCryptoFunction.service.ts b/jslib/node/src/services/nodeCryptoFunction.service.ts new file mode 100644 index 00000000..90a04574 --- /dev/null +++ b/jslib/node/src/services/nodeCryptoFunction.service.ts @@ -0,0 +1,301 @@ +import * as crypto from "crypto"; + +import * as forge from "node-forge"; + +import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; +import { Utils } from "jslib-common/misc/utils"; +import { DecryptParameters } from "jslib-common/models/domain/decryptParameters"; +import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; + +export class NodeCryptoFunctionService implements CryptoFunctionService { + pbkdf2( + password: string | ArrayBuffer, + salt: string | ArrayBuffer, + algorithm: "sha256" | "sha512", + iterations: number + ): Promise { + const len = algorithm === "sha256" ? 32 : 64; + const nodePassword = this.toNodeValue(password); + const nodeSalt = this.toNodeValue(salt); + return new Promise((resolve, reject) => { + crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => { + if (error != null) { + reject(error); + } else { + resolve(this.toArrayBuffer(key)); + } + }); + }); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdf( + ikm: ArrayBuffer, + salt: string | ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const saltBuf = this.toArrayBuffer(salt); + const prk = await this.hmac(ikm, saltBuf, algorithm); + return this.hkdfExpand(prk, info, outputByteSize, algorithm); + } + + // ref: https://tools.ietf.org/html/rfc5869 + async hkdfExpand( + prk: ArrayBuffer, + info: string | ArrayBuffer, + outputByteSize: number, + algorithm: "sha256" | "sha512" + ): Promise { + const hashLen = algorithm === "sha256" ? 32 : 64; + if (outputByteSize > 255 * hashLen) { + throw new Error("outputByteSize is too large."); + } + const prkArr = new Uint8Array(prk); + if (prkArr.length < hashLen) { + throw new Error("prk is too small."); + } + const infoBuf = this.toArrayBuffer(info); + const infoArr = new Uint8Array(infoBuf); + let runningOkmLength = 0; + let previousT = new Uint8Array(0); + const n = Math.ceil(outputByteSize / hashLen); + const okm = new Uint8Array(n * hashLen); + for (let i = 0; i < n; i++) { + const t = new Uint8Array(previousT.length + infoArr.length + 1); + t.set(previousT); + t.set(infoArr, previousT.length); + t.set([i + 1], t.length - 1); + previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + okm.set(previousT, runningOkmLength); + runningOkmLength += previousT.length; + if (runningOkmLength >= outputByteSize) { + break; + } + } + return okm.slice(0, outputByteSize).buffer; + } + + hash( + value: string | ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" | "md5" + ): Promise { + const nodeValue = this.toNodeValue(value); + const hash = crypto.createHash(algorithm); + hash.update(nodeValue); + return Promise.resolve(this.toArrayBuffer(hash.digest())); + } + + hmac( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + const nodeValue = this.toNodeBuffer(value); + const nodeKey = this.toNodeBuffer(key); + const hmac = crypto.createHmac(algorithm, nodeKey); + hmac.update(nodeValue); + return Promise.resolve(this.toArrayBuffer(hmac.digest())); + } + + async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + const key = await this.randomBytes(32); + const mac1 = await this.hmac(a, key, "sha256"); + const mac2 = await this.hmac(b, key, "sha256"); + if (mac1.byteLength !== mac2.byteLength) { + return false; + } + + const arr1 = new Uint8Array(mac1); + const arr2 = new Uint8Array(mac2); + for (let i = 0; i < arr2.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + + return true; + } + + hmacFast( + value: ArrayBuffer, + key: ArrayBuffer, + algorithm: "sha1" | "sha256" | "sha512" + ): Promise { + return this.hmac(value, key, algorithm); + } + + compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise { + return this.compare(a, b); + } + + aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const nodeData = this.toNodeBuffer(data); + const nodeIv = this.toNodeBuffer(iv); + const nodeKey = this.toNodeBuffer(key); + const cipher = crypto.createCipheriv("aes-256-cbc", nodeKey, nodeIv); + const encBuf = Buffer.concat([cipher.update(nodeData), cipher.final()]); + return Promise.resolve(this.toArrayBuffer(encBuf)); + } + + aesDecryptFastParameters( + data: string, + iv: string, + mac: string, + key: SymmetricCryptoKey + ): DecryptParameters { + const p = new DecryptParameters(); + p.encKey = key.encKey; + p.data = Utils.fromB64ToArray(data).buffer; + p.iv = Utils.fromB64ToArray(iv).buffer; + + const macData = new Uint8Array(p.iv.byteLength + p.data.byteLength); + macData.set(new Uint8Array(p.iv), 0); + macData.set(new Uint8Array(p.data), p.iv.byteLength); + p.macData = macData.buffer; + + if (key.macKey != null) { + p.macKey = key.macKey; + } + if (mac != null) { + p.mac = Utils.fromB64ToArray(mac).buffer; + } + + return p; + } + + async aesDecryptFast(parameters: DecryptParameters): Promise { + const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey); + return Utils.fromBufferToUtf8(decBuf); + } + + aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + const nodeData = this.toNodeBuffer(data); + const nodeIv = this.toNodeBuffer(iv); + const nodeKey = this.toNodeBuffer(key); + const decipher = crypto.createDecipheriv("aes-256-cbc", nodeKey, nodeIv); + const decBuf = Buffer.concat([decipher.update(nodeData), decipher.final()]); + return Promise.resolve(this.toArrayBuffer(decBuf)); + } + + rsaEncrypt( + data: ArrayBuffer, + publicKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + if (algorithm === "sha256") { + throw new Error("Node crypto does not support RSA-OAEP SHA-256"); + } + + const pem = this.toPemPublicKey(publicKey); + const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data)); + return Promise.resolve(this.toArrayBuffer(decipher)); + } + + rsaDecrypt( + data: ArrayBuffer, + privateKey: ArrayBuffer, + algorithm: "sha1" | "sha256" + ): Promise { + if (algorithm === "sha256") { + throw new Error("Node crypto does not support RSA-OAEP SHA-256"); + } + + const pem = this.toPemPrivateKey(privateKey); + const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data)); + return Promise.resolve(this.toArrayBuffer(decipher)); + } + + rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const privateKeyByteString = Utils.fromBufferToByteString(privateKey); + const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString); + const forgePrivateKey: any = forge.pki.privateKeyFromAsn1(privateKeyAsn1); + const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e); + const publicKeyAsn1 = forge.pki.publicKeyToAsn1(forgePublicKey); + const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data; + const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString); + return Promise.resolve(publicKeyArray.buffer); + } + + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { + return new Promise<[ArrayBuffer, ArrayBuffer]>((resolve, reject) => { + forge.pki.rsa.generateKeyPair( + { + bits: length, + workers: -1, + e: 0x10001, // 65537 + }, + (error, keyPair) => { + if (error != null) { + reject(error); + return; + } + + const publicKeyAsn1 = forge.pki.publicKeyToAsn1(keyPair.publicKey); + const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).getBytes(); + const publicKey = Utils.fromByteStringToArray(publicKeyByteString); + + const privateKeyAsn1 = forge.pki.privateKeyToAsn1(keyPair.privateKey); + const privateKeyPkcs8 = forge.pki.wrapRsaPrivateKey(privateKeyAsn1); + const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes(); + const privateKey = Utils.fromByteStringToArray(privateKeyByteString); + + resolve([publicKey.buffer, privateKey.buffer]); + } + ); + }); + } + + randomBytes(length: number): Promise { + return new Promise((resolve, reject) => { + crypto.randomBytes(length, (error, bytes) => { + if (error != null) { + reject(error); + } else { + resolve(this.toArrayBuffer(bytes)); + } + }); + }); + } + + private toNodeValue(value: string | ArrayBuffer): string | Buffer { + let nodeValue: string | Buffer; + if (typeof value === "string") { + nodeValue = value; + } else { + nodeValue = this.toNodeBuffer(value); + } + return nodeValue; + } + + private toNodeBuffer(value: ArrayBuffer): Buffer { + return Buffer.from(new Uint8Array(value) as any); + } + + private toArrayBuffer(value: Buffer | string | ArrayBuffer): ArrayBuffer { + let buf: ArrayBuffer; + if (typeof value === "string") { + buf = Utils.fromUtf8ToArray(value).buffer; + } else { + buf = new Uint8Array(value).buffer; + } + return buf; + } + + private toPemPrivateKey(key: ArrayBuffer): string { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + const privateKey = forge.pki.privateKeyFromAsn1(asn1); + const rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey); + const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey); + return forge.pki.privateKeyInfoToPem(privateKeyInfo); + } + + private toPemPublicKey(key: ArrayBuffer): string { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + const publicKey = forge.pki.publicKeyFromAsn1(asn1); + return forge.pki.publicKeyToPem(publicKey); + } +} diff --git a/jslib/node/tsconfig.json b/jslib/node/tsconfig.json new file mode 100644 index 00000000..f544d7ce --- /dev/null +++ b/jslib/node/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../shared/tsconfig", + "compilerOptions": { + "paths": { + "jslib-common/*": ["../common/src/*"], + "jslib-node/*": ["./src/*"] + } + }, + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/node/tsconfig.spec.json b/jslib/node/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/node/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/jslib/package-lock.json b/jslib/package-lock.json new file mode 100644 index 00000000..40597c1b --- /dev/null +++ b/jslib/package-lock.json @@ -0,0 +1,36691 @@ +{ + "name": "@bitwarden/jslib", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@bitwarden/jslib", + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-angular": "file:angular", + "@bitwarden/jslib-common": "file:common", + "@bitwarden/jslib-electron": "file:electron", + "@bitwarden/jslib-node": "file:node" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^12.2.13", + "@angular/compiler-cli": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@fluffy-spoon/substitute": "^1.202.0", + "@types/jest": "^27.4.1", + "@types/node": "^16.11.12", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", + "commander": "7.2.0", + "concurrently": "^6.1.0", + "eslint": "^8.7.0", + "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "form-data": "4.0.0", + "husky": "^7.0.4", + "jest": "^27.5.1", + "jest-preset-angular": "^11.1.1", + "jsdom": "^16.5.3", + "lint-staged": "^12.1.2", + "node-forge": "^1.2.0", + "nodemon": "^2.0.7", + "prettier": "2.5.1", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "ts-loader": "^8.1.0", + "ts-node": "^10.4.0", + "tsconfig-paths": "^3.12.0", + "ttypescript": "^1.5.12", + "typemoq": "^2.1.0", + "typescript": "4.3.5", + "typescript-transform-paths": "^2.2.3", + "zone.js": "0.11.4" + }, + "engines": { + "node": "~16", + "npm": "~8" + } + }, + "angular": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zone.js": "0.11.4" + }, + "devDependencies": { + "@types/duo_web_sdk": "^2.7.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "common": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "zxcvbn": "^4.4.2" + }, + "devDependencies": { + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "electron": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0" + }, + "devDependencies": { + "@types/node": "^16.11.12", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node": { + "version": "0.0.0", + "license": "GPL-3.0", + "dependencies": { + "@bitwarden/jslib-common": "file:../common", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1" + }, + "devDependencies": { + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz", + "integrity": "sha512-Ta9bMA3EtUHDaZJXqUoT5cn/EecwOp+SXpKJqxDbDuMbLvEMu6YTyDDuvTWeStODfdmXyfMo7LymQyPkN3BicA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "1.0.0", + "sourcemap-codec": "1.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.17.tgz", + "integrity": "sha512-uUQcHcLbPvr9adALQSLU1MTDduVUR2kZAHi2e7SmL9ioel84pPVXBoD0WpSBeUMKwPiDs3TQDaxDB49hl0nBSQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "12.2.17", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.17.tgz", + "integrity": "sha512-uc3HGHVQyatqQ/M53oxYBvhz0jx0hgdc7WT+L56GLHvgz7Ct2VEbpWaMfwHkFfE1F1iHkIgnTKHKWacJl1yQIg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "1.0.1", + "@angular-devkit/architect": "0.1202.17", + "@angular-devkit/build-optimizer": "0.1202.17", + "@angular-devkit/build-webpack": "0.1202.17", + "@angular-devkit/core": "12.2.17", + "@babel/core": "7.14.8", + "@babel/generator": "7.14.8", + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/plugin-proposal-async-generator-functions": "7.14.7", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/plugin-transform-runtime": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/runtime": "7.14.8", + "@babel/template": "7.14.5", + "@discoveryjs/json-ext": "0.5.3", + "@jsdevtools/coverage-istanbul-loader": "3.0.5", + "@ngtools/webpack": "12.2.17", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.2", + "browserslist": "^4.9.1", + "cacache": "15.2.0", + "caniuse-lite": "^1.0.30001032", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "9.0.1", + "core-js": "3.16.0", + "critters": "0.0.12", + "css-loader": "6.2.0", + "css-minimizer-webpack-plugin": "3.0.2", + "esbuild-wasm": "0.13.8", + "find-cache-dir": "3.3.1", + "glob": "7.1.7", + "https-proxy-agent": "5.0.0", + "inquirer": "8.1.2", + "karma-source-map-support": "1.4.0", + "less": "4.1.1", + "less-loader": "10.0.1", + "license-webpack-plugin": "2.3.20", + "loader-utils": "2.0.0", + "mini-css-extract-plugin": "2.4.2", + "minimatch": "3.0.4", + "open": "8.2.1", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.1.0", + "postcss": "8.3.6", + "postcss-import": "14.0.2", + "postcss-loader": "6.1.1", + "postcss-preset-env": "6.7.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "4.0.0", + "rxjs": "6.6.7", + "sass": "1.36.0", + "sass-loader": "12.1.0", + "semver": "7.3.5", + "source-map-loader": "3.0.0", + "source-map-support": "0.5.19", + "style-loader": "3.2.1", + "stylus": "0.54.8", + "stylus-loader": "6.1.0", + "terser": "5.7.1", + "terser-webpack-plugin": "5.1.4", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.0", + "webpack": "5.50.0", + "webpack-dev-middleware": "5.0.0", + "webpack-dev-server": "3.11.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "1.5.2" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.13.8" + }, + "peerDependencies": { + "@angular/compiler-cli": "^12.0.0", + "@angular/localize": "^12.0.0", + "@angular/service-worker": "^12.0.0", + "karma": "^6.3.0", + "ng-packagr": "^12.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0", + "tslint": "^6.1.0", + "typescript": "~4.2.3 || ~4.3.2" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "tslint": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/es-module-lexer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", + "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz", + "integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.7.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@angular-devkit/build-optimizer": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.17.tgz", + "integrity": "sha512-1qWGWw7cCNADB4LZ/zjiSK0GLmr2kebYyNG0KutCE8GNVxv2h6w6dJP6t1C/BgskRuBPCAhvE+lEKN8ljSutag==", + "dev": true, + "dependencies": { + "source-map": "0.7.3", + "tslib": "2.3.0", + "typescript": "4.3.5" + }, + "bin": { + "build-optimizer": "src/build-optimizer/cli.js" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.17.tgz", + "integrity": "sha512-z7FW43DJ4p8UZwbFRmMrh2ohqhI2Wtdg3+FZiTnl4opb3zYheGiNxPlTuiyKjG21JUkGCdthkkBLCNfaUU0U/Q==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1202.17", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^3.1.4" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/core": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.17.tgz", + "integrity": "sha512-PyOY7LGUPPd6rakxUYbfQN6zAdOCMCouVp5tERY1WTdMdEiuULOtHsPee8kNbh75pD59KbJNU+fwozPRMuIm5g==", + "dev": true, + "dependencies": { + "ajv": "8.6.2", + "ajv-formats": "2.1.0", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular/animations": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz", + "integrity": "sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.16" + } + }, + "node_modules/@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^12.0.0 || ^13.0.0-0", + "@angular/core": "^12.0.0 || ^13.0.0-0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/@angular/common": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz", + "integrity": "sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/core": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/compiler": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz", + "integrity": "sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz", + "integrity": "sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", + "canonical-path": "1.0.0", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.2.0", + "yargs": "^17.0.0" + }, + "bin": { + "ivy-ngcc": "ngcc/main-ivy-ngcc.js", + "ng-xi18n": "src/extract_i18n.js", + "ngc": "src/main.js", + "ngcc": "ngcc/main-ngcc.js" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/compiler": "12.2.16", + "typescript": ">=4.2.3 <4.4" + } + }, + "node_modules/@angular/compiler-cli/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular/core": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz", + "integrity": "sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.0.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz", + "integrity": "sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz", + "integrity": "sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/animations": "12.2.16", + "@angular/common": "12.2.16", + "@angular/core": "12.2.16" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz", + "integrity": "sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/compiler": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16" + } + }, + "node_modules/@angular/router": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz", + "integrity": "sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": "^12.14.1 || >=14.0.0" + }, + "peerDependencies": { + "@angular/common": "12.2.16", + "@angular/core": "12.2.16", + "@angular/platform-browser": "12.2.16", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.8", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", + "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz", + "integrity": "sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.8.tgz", + "integrity": "sha512-a9aOppDU93oArQ51H+B8M1vH+tayZbuBqzjOhntGetZVa+4tTu5jp+XTwqHGG2lxslqomPYVSjIxQkFwXzgnxg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.14.7", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.14.5", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.14.5", + "@babel/plugin-transform-classes": "^7.14.5", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.14.5", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.14.5", + "@babel/plugin-transform-modules-systemjs": "^7.14.5", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.14.5", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.14.8", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.15.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@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 + }, + "node_modules/@bitwarden/jslib-angular": { + "resolved": "angular", + "link": true + }, + "node_modules/@bitwarden/jslib-common": { + "resolved": "common", + "link": true + }, + "node_modules/@bitwarden/jslib-electron": { + "resolved": "electron", + "link": true + }, + "node_modules/@bitwarden/jslib-node": { + "resolved": "node", + "link": true + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=8.6" + }, + "optionalDependencies": { + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@fluffy-spoon/substitute": { + "version": "1.208.0", + "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", + "integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/substitute-js#section-contribute" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@jest/reporters/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz", + "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jsdevtools/coverage-istanbul-loader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", + "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", + "dev": true, + "dependencies": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.7.0" + } + }, + "node_modules/@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + } + }, + "node_modules/@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "dependencies": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "node_modules/@microsoft/signalr/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/@ngtools/webpack": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.17.tgz", + "integrity": "sha512-uaS+2YZgPDW3VmUuwh4/yfIFV1KRVGWefc6xLWIqKRKs6mlRYs65m3ib9dX7CTS4kQMCbhxkxMbpBO2yXlzfvA==", + "dev": true, + "engines": { + "node": "^12.14.1 || >=14.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^12.0.0", + "typescript": "~4.2.3 || ~4.3.2", + "webpack": "^5.30.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@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==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "node_modules/@types/inquirer/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@types/inquirer/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@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, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.14.181", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz", + "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==", + "dev": true + }, + "node_modules/@types/lowdb": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.11.tgz", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "node_modules/@types/webpack-sources": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz", + "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + } + }, + "node_modules/@types/webpack-sources/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", + "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/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, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/autoprefixer/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/autoprefixer/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "node_modules/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 + }, + "node_modules/browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/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, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "dependencies": { + "debug": "^4.3.2", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "dev": true, + "dependencies": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/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, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/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==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001325", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", + "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/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 + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/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==" + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conf": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.2.tgz", + "integrity": "sha512-o9Fv1Mv+6A0JpoayQ8JleNp3hhkbOJP/Re/Q+QqxMPHPkABVsRjQGWZn9A5GcqLiTNC6d89p2PB5ZhHVDSMwyg==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/conf/node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", + "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "glob-parent": "^6.0.0", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/critters": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", + "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.1.3", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/critters/node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-blank-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-blank-pseudo/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-blank-pseudo/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-blank-pseudo/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "bin": { + "css-has-pseudo": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-has-pseudo/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-has-pseudo/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-has-pseudo/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-has-pseudo/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", + "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==", + "dev": true, + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "p-limit": "^3.0.2", + "postcss": "^8.3.5", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "dependencies": { + "css": "^2.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "bin": { + "css-prefers-color-scheme": "cli.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-prefers-color-scheme/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/css-prefers-color-scheme/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-prefers-color-scheme/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.7", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.2.2", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.4", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "dev": true, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debounce-fn/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/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, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/default-gateway/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/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, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/default-gateway/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/default-gateway/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/default-gateway/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/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==" + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "devOptional": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "devOptional": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/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, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duo_web_sdk": { + "version": "2.7.0", + "resolved": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 8.6" + } + }, + "node_modules/electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "node_modules/electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "dependencies": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-store/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "dev": true + }, + "node_modules/electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "dependencies": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/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==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "devOptional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/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, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true, + "peer": true + }, + "node_modules/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, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz", + "integrity": "sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.13.8", + "esbuild-darwin-64": "0.13.8", + "esbuild-darwin-arm64": "0.13.8", + "esbuild-freebsd-64": "0.13.8", + "esbuild-freebsd-arm64": "0.13.8", + "esbuild-linux-32": "0.13.8", + "esbuild-linux-64": "0.13.8", + "esbuild-linux-arm": "0.13.8", + "esbuild-linux-arm64": "0.13.8", + "esbuild-linux-mips64le": "0.13.8", + "esbuild-linux-ppc64le": "0.13.8", + "esbuild-netbsd-64": "0.13.8", + "esbuild-openbsd-64": "0.13.8", + "esbuild-sunos-64": "0.13.8", + "esbuild-windows-32": "0.13.8", + "esbuild-windows-64": "0.13.8", + "esbuild-windows-arm64": "0.13.8" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz", + "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz", + "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz", + "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz", + "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz", + "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz", + "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz", + "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz", + "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz", + "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz", + "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz", + "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz", + "integrity": "sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz", + "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz", + "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz", + "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-wasm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz", + "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz", + "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz", + "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz", + "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/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==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dependencies": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/extract-zip/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/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==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "dependencies": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + } + }, + "node_modules/fetch-cookie/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/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=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "dependencies": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/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, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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==" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/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 + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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 + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/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 + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-preset-angular": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-11.1.1.tgz", + "integrity": "sha512-ZlYiKJhAQSU9wIjncX59xutcj49R4MiDsTPSwZiwdTAHQvHm32MS6SGimQIVBqh1DukfwYX0NXKS0D/onLAsLQ==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "esbuild-wasm": "0.14.11", + "jest-environment-jsdom": "^27.0.0", + "pretty-format": "^27.0.0", + "ts-jest": "^27.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": "0.14.11" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=0.1002.4", + "@angular/compiler-cli": ">=10.0.0", + "@angular/core": ">=10.0.0", + "@angular/platform-browser-dynamic": ">=10.0.0" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.11.tgz", + "integrity": "sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.11", + "esbuild-darwin-64": "0.14.11", + "esbuild-darwin-arm64": "0.14.11", + "esbuild-freebsd-64": "0.14.11", + "esbuild-freebsd-arm64": "0.14.11", + "esbuild-linux-32": "0.14.11", + "esbuild-linux-64": "0.14.11", + "esbuild-linux-arm": "0.14.11", + "esbuild-linux-arm64": "0.14.11", + "esbuild-linux-mips64le": "0.14.11", + "esbuild-linux-ppc64le": "0.14.11", + "esbuild-linux-s390x": "0.14.11", + "esbuild-netbsd-64": "0.14.11", + "esbuild-openbsd-64": "0.14.11", + "esbuild-sunos-64": "0.14.11", + "esbuild-windows-32": "0.14.11", + "esbuild-windows-64": "0.14.11", + "esbuild-windows-arm64": "0.14.11" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild-android-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz", + "integrity": "sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-darwin-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz", + "integrity": "sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-darwin-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz", + "integrity": "sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-freebsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz", + "integrity": "sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-freebsd-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz", + "integrity": "sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz", + "integrity": "sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz", + "integrity": "sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-arm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz", + "integrity": "sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz", + "integrity": "sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-mips64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz", + "integrity": "sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-linux-ppc64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz", + "integrity": "sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-netbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz", + "integrity": "sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-openbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz", + "integrity": "sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-sunos-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz", + "integrity": "sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-wasm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.11.tgz", + "integrity": "sha512-9e1R6hv0hiU+BkJI2edqUuWfXUbOP2Mox+Ijl/uY1vLLlSsunkrcADqD/4Rz+VCEDzw6ecscJM+uJqR2fRmEUg==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz", + "integrity": "sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz", + "integrity": "sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-preset-angular/node_modules/esbuild-windows-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz", + "integrity": "sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "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.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "node_modules/less": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", + "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz", + "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "2.3.20", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz", + "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==", + "dev": true, + "dependencies": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "12.3.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.7.tgz", + "integrity": "sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/lint-staged/node_modules/lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lint-staged/node_modules/supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "node_modules/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 + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "dependencies": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/memory-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-source-map/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz", + "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==", + "dev": true, + "dependencies": { + "schema-utils": "^3.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "dependencies": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/msgpack5/node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/msgpack5/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/msgpack5/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/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/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "node_modules/nodemon": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", + "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5", + "update-notifier": "^5.1.0" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "devOptional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz", + "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/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==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-functional-notation/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-functional-notation/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-functional-notation/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-gray/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-gray/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-gray/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-hex-alpha/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-hex-alpha/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-hex-alpha/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-mod-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-mod-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-mod-function/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-color-rebeccapurple/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-color-rebeccapurple/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-color-rebeccapurple/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-media/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-media/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-media/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-properties/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-properties/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-properties/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-custom-selectors/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-custom-selectors/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-double-position-gradients/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-double-position-gradients/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-double-position-gradients/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-env-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-env-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-env-function/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-focus-visible/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-focus-visible/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-focus-visible/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-focus-within/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-focus-within/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-focus-within/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-font-variant/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-font-variant/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-font-variant/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-gap-properties/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-gap-properties/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-gap-properties/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-image-set-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-image-set-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-image-set-function/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-initial/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-initial/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-initial/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dev": true, + "dependencies": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-lab-function/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-lab-function/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-lab-function/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz", + "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-logical/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-logical/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-logical/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-media-minmax/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-media-minmax/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-media-minmax/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-nesting/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-nesting/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-overflow-shorthand/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-overflow-shorthand/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-overflow-shorthand/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-page-break/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-page-break/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-page-break/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-place/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-place/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-place/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dev": true, + "dependencies": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-preset-env/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-preset-env/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-preset-env/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "dependencies": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-replace-overflow-wrap/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-matches/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-selector-matches/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-matches/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "node_modules/postcss-selector-not/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-selector-not/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-not/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/postcss/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/postinstall-build": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", + "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", + "deprecated": "postinstall-build's behavior is now built into npm! You should migrate off of postinstall-build and use the new `prepare` lifecycle script with npm 5.0.0 or greater.", + "dev": true, + "bin": { + "postinstall-build": "cli.js" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/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 + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/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==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz", + "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", + "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/selfsigned/node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz", + "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "dependencies": { + "graceful-fs": "^4.1.3" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", + "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dev": true, + "dependencies": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz", + "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/stylus/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/stylus/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/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 + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/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, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", + "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "dev": true, + "dependencies": { + "jest-worker": "^27.0.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "hasInstallScript": true, + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tldjs/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-jest": { + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "*" + } + }, + "node_modules/ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "dependencies": { + "resolve": ">=1.9.0" + }, + "bin": { + "ttsc": "bin/tsc", + "ttsserver": "bin/tsserver" + }, + "peerDependencies": { + "ts-node": ">=8.0.2", + "typescript": ">=3.2.2" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typemoq": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", + "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "circular-json": "^0.3.1", + "lodash": "^4.17.4", + "postinstall-build": "^5.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-transform-paths": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-2.2.4.tgz", + "integrity": "sha512-i+/sgp3rw1ZronMCm2TKGBy1dlvN88Kd8CCb+HWnOE8+Hv0uIVnbC8xM5AD2t1JBCWabEhuH9p3n8DOVi0+R6g==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "peerDependencies": { + "typescript": ">=3.6.5" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/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, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.71.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.71.0.tgz", + "integrity": "sha512-g4dFT7CFG8LY0iU5G8nBL6VlkT21Z7dcYDpJAEJV5Q1WLb9UwnFbrem1k7K52ILqEmomN7pnzWFxxE6SlDY56A==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.0.0.tgz", + "integrity": "sha512-9zng2Z60pm6A98YoRcA0wSxw1EYn7B7y5owX/Tckyt9KGyULTkLtiavjaXlWqOMkM0YtqGgL3PvMOFgyFLq8vw==", + "dev": true, + "dependencies": { + "colorette": "^1.2.2", + "mem": "^8.1.1", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack-dev-middleware/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "dependencies": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/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, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/webpack-dev-server/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/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 + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/webpack-dev-server/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/webpack-dev-server/node_modules/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, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-log/node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-log/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz", + "integrity": "sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw==", + "dev": true, + "dependencies": { + "webpack-sources": "^1.3.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 2.21.0 < 5", + "webpack": ">= 1.12.11 < 6" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/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 + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + }, + "node/node_modules/inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-1.0.1.tgz", + "integrity": "sha512-Ta9bMA3EtUHDaZJXqUoT5cn/EecwOp+SXpKJqxDbDuMbLvEMu6YTyDDuvTWeStODfdmXyfMo7LymQyPkN3BicA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "1.0.0", + "sourcemap-codec": "1.4.8" + } + }, + "@angular-devkit/architect": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.17.tgz", + "integrity": "sha512-uUQcHcLbPvr9adALQSLU1MTDduVUR2kZAHi2e7SmL9ioel84pPVXBoD0WpSBeUMKwPiDs3TQDaxDB49hl0nBSQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "12.2.17", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.17.tgz", + "integrity": "sha512-uc3HGHVQyatqQ/M53oxYBvhz0jx0hgdc7WT+L56GLHvgz7Ct2VEbpWaMfwHkFfE1F1iHkIgnTKHKWacJl1yQIg==", + "dev": true, + "requires": { + "@ampproject/remapping": "1.0.1", + "@angular-devkit/architect": "0.1202.17", + "@angular-devkit/build-optimizer": "0.1202.17", + "@angular-devkit/build-webpack": "0.1202.17", + "@angular-devkit/core": "12.2.17", + "@babel/core": "7.14.8", + "@babel/generator": "7.14.8", + "@babel/helper-annotate-as-pure": "7.14.5", + "@babel/plugin-proposal-async-generator-functions": "7.14.7", + "@babel/plugin-transform-async-to-generator": "7.14.5", + "@babel/plugin-transform-runtime": "7.14.5", + "@babel/preset-env": "7.14.8", + "@babel/runtime": "7.14.8", + "@babel/template": "7.14.5", + "@discoveryjs/json-ext": "0.5.3", + "@jsdevtools/coverage-istanbul-loader": "3.0.5", + "@ngtools/webpack": "12.2.17", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.2", + "browserslist": "^4.9.1", + "cacache": "15.2.0", + "caniuse-lite": "^1.0.30001032", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "9.0.1", + "core-js": "3.16.0", + "critters": "0.0.12", + "css-loader": "6.2.0", + "css-minimizer-webpack-plugin": "3.0.2", + "esbuild": "0.13.8", + "esbuild-wasm": "0.13.8", + "find-cache-dir": "3.3.1", + "glob": "7.1.7", + "https-proxy-agent": "5.0.0", + "inquirer": "8.1.2", + "karma-source-map-support": "1.4.0", + "less": "4.1.1", + "less-loader": "10.0.1", + "license-webpack-plugin": "2.3.20", + "loader-utils": "2.0.0", + "mini-css-extract-plugin": "2.4.2", + "minimatch": "3.0.4", + "open": "8.2.1", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.1.0", + "postcss": "8.3.6", + "postcss-import": "14.0.2", + "postcss-loader": "6.1.1", + "postcss-preset-env": "6.7.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "4.0.0", + "rxjs": "6.6.7", + "sass": "1.36.0", + "sass-loader": "12.1.0", + "semver": "7.3.5", + "source-map-loader": "3.0.0", + "source-map-support": "0.5.19", + "style-loader": "3.2.1", + "stylus": "0.54.8", + "stylus-loader": "6.1.0", + "terser": "5.7.1", + "terser-webpack-plugin": "5.1.4", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.0", + "webpack": "5.50.0", + "webpack-dev-middleware": "5.0.0", + "webpack-dev-server": "3.11.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "1.5.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "es-module-lexer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", + "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "webpack": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz", + "integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.0", + "es-module-lexer": "^0.7.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + } + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.17.tgz", + "integrity": "sha512-1qWGWw7cCNADB4LZ/zjiSK0GLmr2kebYyNG0KutCE8GNVxv2h6w6dJP6t1C/BgskRuBPCAhvE+lEKN8ljSutag==", + "dev": true, + "requires": { + "source-map": "0.7.3", + "tslib": "2.3.0", + "typescript": "4.3.5" + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1202.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.17.tgz", + "integrity": "sha512-z7FW43DJ4p8UZwbFRmMrh2ohqhI2Wtdg3+FZiTnl4opb3zYheGiNxPlTuiyKjG21JUkGCdthkkBLCNfaUU0U/Q==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1202.17", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.17.tgz", + "integrity": "sha512-PyOY7LGUPPd6rakxUYbfQN6zAdOCMCouVp5tERY1WTdMdEiuULOtHsPee8kNbh75pD59KbJNU+fwozPRMuIm5g==", + "dev": true, + "requires": { + "ajv": "8.6.2", + "ajv-formats": "2.1.0", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.16.tgz", + "integrity": "sha512-Kf6C7Ta+fCMq5DvT9JNVhBkcECrqFa3wumiC6ssGo5sNaEzXz+tlep9ZgEbqfxSn7gAN7L1DgsbS9u0O6tbUkg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/cdk": { + "version": "12.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.13.tgz", + "integrity": "sha512-zSKRhECyFqhingIeyRInIyTvYErt4gWo+x5DQr0b7YLUbU8DZSwWnG4w76Ke2s4U8T7ry1jpJBHoX/e8YBpGMg==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + } + } + }, + "@angular/common": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.16.tgz", + "integrity": "sha512-FEqTXTEsnbDInqV1yFlm97Tz1OFqZS5t0TUkm8gzXRgpIce/F/jLwAg0u1VQkgOsno6cNm0xTWPoZgu85NI4ug==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.16.tgz", + "integrity": "sha512-nsYEw+yu8QyeqPf9nAmG419i1mtGM4v8+U+S3eQHQFXTgJzLymMykWHYu2ETdjUpNSLK6xcIQDBWtWnWSfJjAA==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/compiler-cli": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.16.tgz", + "integrity": "sha512-tlalh8SJvdCWbUPRUR5GamaP+wSc/GuCsoUZpSbcczGKgSlbaEVXUYtVXm8/wuT6Slk2sSEbRs7tXGF2i7qxVw==", + "dev": true, + "requires": { + "@babel/core": "^7.8.6", + "@babel/types": "^7.8.6", + "canonical-path": "1.0.0", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.2.0", + "yargs": "^17.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==", + "dev": true + } + } + }, + "@angular/core": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.16.tgz", + "integrity": "sha512-jsmvaRdAfng99z2a9mAmkfcsCE1wm+tBYVDxnc5JquSXznwtncjzcoc2X0J0dzrkCDvzFfpTsZ9vehylytBc+A==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/forms": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.16.tgz", + "integrity": "sha512-sb+gpNun5aN7CZfHXS6X7vJcd/0A1P/gRBZpYtQTzBYnqEFCOFIvR62eb05aHQ4JhgKaSPpIXrbz/bAwY/njZw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.16.tgz", + "integrity": "sha512-T855ppLeQO6hRHi7lGf5fwPoUVt+c0h2rgkV5jHElc3ylaGnhecmZc6fnWLX4pw82TMJUgUV88CY8JCFabJWwg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.16.tgz", + "integrity": "sha512-XGxoACAMW/bc3atiVRpaiYwU4LkobYwVzwlxTT/BxOfsdt8ILb5wU8Fx1TMKNECOQHSGdK0qqhch4pTBZ3cb2g==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@angular/router": { + "version": "12.2.16", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.16.tgz", + "integrity": "sha512-LuFXSMIvX/VrB4jbYhigG2Y2pGQ9ULsSBUwDWwQCf4kr0eVI37LBJ2Vr74GBEznjgQ0UmWE89E+XYI80UhERTw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true + }, + "@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.8", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", + "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "dev": true, + "requires": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz", + "integrity": "sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@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.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@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-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz", + "integrity": "sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/preset-env": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.8.tgz", + "integrity": "sha512-a9aOppDU93oArQ51H+B8M1vH+tayZbuBqzjOhntGetZVa+4tTu5jp+XTwqHGG2lxslqomPYVSjIxQkFwXzgnxg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.14.7", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-async-generator-functions": "^7.14.7", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.14.5", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.14.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.14.5", + "@babel/plugin-transform-classes": "^7.14.5", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.14.5", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.14.5", + "@babel/plugin-transform-modules-systemjs": "^7.14.5", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.7", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.14.5", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.14.6", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.14.8", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.2", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.15.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "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 + }, + "@bitwarden/jslib-angular": { + "version": "file:angular", + "requires": { + "@angular/animations": "^12.2.13", + "@angular/cdk": "^12.2.13", + "@angular/common": "^12.2.13", + "@angular/compiler": "^12.2.13", + "@angular/core": "^12.2.13", + "@angular/forms": "^12.2.13", + "@angular/platform-browser": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@angular/router": "^12.2.13", + "@bitwarden/jslib-common": "file:../common", + "@types/duo_web_sdk": "^2.7.1", + "duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zone.js": "0.11.4" + } + }, + "@bitwarden/jslib-common": { + "version": "file:common", + "requires": { + "@microsoft/signalr": "5.0.10", + "@microsoft/signalr-protocol-msgpack": "5.0.10", + "@types/lunr": "^2.3.3", + "@types/node": "^16.11.12", + "@types/node-forge": "^1.0.1", + "@types/papaparse": "^5.2.5", + "@types/tldjs": "^2.3.0", + "@types/zxcvbn": "^4.4.1", + "big-integer": "1.6.48", + "browser-hrtime": "^1.1.8", + "lunr": "^2.3.9", + "node-forge": "^1.2.1", + "papaparse": "^5.3.0", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "tldjs": "^2.3.1", + "typescript": "4.3.5", + "zxcvbn": "^4.4.2" + } + }, + "@bitwarden/jslib-electron": { + "version": "file:electron", + "requires": { + "@bitwarden/jslib-common": "file:../common", + "@types/node": "^16.11.12", + "electron": "16.1.0", + "electron-log": "4.4.6", + "electron-store": "8.0.1", + "electron-updater": "5.0.0", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + } + }, + "@bitwarden/jslib-node": { + "version": "file:node", + "requires": { + "@bitwarden/jslib-common": "file:../common", + "@types/inquirer": "^7.3.1", + "@types/lowdb": "^1.0.10", + "@types/node": "^16.11.12", + "@types/node-fetch": "^2.5.10", + "chalk": "^4.1.1", + "commander": "7.2.0", + "form-data": "4.0.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.0.0", + "lowdb": "1.0.0", + "node-fetch": "^2.6.1", + "rimraf": "^3.0.2", + "typescript": "4.3.5" + }, + "dependencies": { + "inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", + "dev": true + }, + "@discoveryjs/json-ext": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", + "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "dev": true + }, + "@electron/get": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz", + "integrity": "sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==", + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "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" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@fluffy-spoon/substitute": { + "version": "1.208.0", + "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", + "integrity": "sha512-BU5vKRoK4OYlKzDtyg4HbtWnUNLOvV0ntqEZIphz+mq2G0HlVFywwJ7M+FbIcnJVDbUReS01FyL5x8R01r7zBg==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@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": { + "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": { + "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" + } + }, + "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" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^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-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": "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 + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.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==", + "dev": true + } + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@jridgewell/resolve-uri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-1.0.0.tgz", + "integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==", + "dev": true + }, + "@jsdevtools/coverage-istanbul-loader": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", + "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", + "dev": true, + "requires": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.3", + "loader-utils": "^2.0.0", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.7.0" + } + }, + "@microsoft/signalr": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.10.tgz", + "integrity": "sha512-7jg6s/cmULyeVvt5/bTB4N9T30HvAF1S06hL+nPcQMODXcclRo34Zcli/dfTLR8lCX31/cVEOmVgxXBOVRQ+Dw==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.7.3", + "node-fetch": "^2.6.0", + "ws": "^6.0.0" + }, + "dependencies": { + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "@microsoft/signalr-protocol-msgpack": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-5.0.10.tgz", + "integrity": "sha512-HqZiNLyjYP1ONeLgYUjFBUsnhxSp5CW4AW8InsLI7lyAXZl2drUhkiBxf3xK9UsTErO1+9r5sdaYdSmUY8nx9A==", + "requires": { + "@microsoft/signalr": ">=5.0.10", + "msgpack5": "^4.5.0" + } + }, + "@ngtools/webpack": { + "version": "12.2.17", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.17.tgz", + "integrity": "sha512-uaS+2YZgPDW3VmUuwh4/yfIFV1KRVGWefc6xLWIqKRKs6mlRYs65m3ib9dX7CTS4kQMCbhxkxMbpBO2yXlzfvA==", + "dev": true, + "requires": {} + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "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==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "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.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/duo_web_sdk": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz", + "integrity": "sha512-DePanZjFww36yGSxXwC8B3AsjrrDuPxEcufeh4gTqVsUMpCYByxjX4PERiYZdW0typzKSt9E4I14PPp+PrSIQA==", + "dev": true + }, + "@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "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.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "27.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", + "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", + "dev": true, + "requires": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/lodash": { + "version": "4.14.181", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz", + "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==", + "dev": true + }, + "@types/lowdb": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.11.tgz", + "integrity": "sha512-h99VMxvTuz+VsXUVCCJo4dsps4vbkXwvU71TpmxDoiBU24bJ0VBygIHgmMm+UPoQIFihmV6euRik4z8J7XDJWg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lunr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz", + "integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/node": { + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/node-forge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-BNbCHJkTE4RwmAFkCxEalET4mDvGr/1ld7ZtQ4i/laWI/iiVt+GL07stdvufle4KfywyvloqqpIiJscXNCrKxA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/prettier": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", + "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", + "dev": true + }, + "@types/semver": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz", + "integrity": "sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==" + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-BQR04zLE0ve2eNrqxXw/Qp/f6LxvNrj/4A8ZgdQi3SzbBqxFhleI7N4DS/mSjDnODrUaEGgoWg4grAZR1kVj8w==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz", + "integrity": "sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "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 + } + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@types/zxcvbn": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.1.tgz", + "integrity": "sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.17.0.tgz", + "integrity": "sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/type-utils": "5.17.0", + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.17.0.tgz", + "integrity": "sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.17.0.tgz", + "integrity": "sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.17.0.tgz", + "integrity": "sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.17.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.17.0.tgz", + "integrity": "sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz", + "integrity": "sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/visitor-keys": "5.17.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.17.0.tgz", + "integrity": "sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.17.0", + "@typescript-eslint/types": "5.17.0", + "@typescript-eslint/typescript-estree": "5.17.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz", + "integrity": "sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.17.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "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 + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "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" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "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 + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "requires": {} + }, + "ajv-formats": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", + "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "requires": { + "string-width": "^4.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": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "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-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "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 + }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, + "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 + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "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 + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" + }, + "autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dev": true, + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "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": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "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-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "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": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "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" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "optional": true + }, + "boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "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": "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" + } + }, + "browser-hrtime": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", + "integrity": "sha512-kzXheikaJsBtzUBlyVtPIY5r0soQePzjwVwT4IlDpU2RvfB5Py52gpU98M77rgqMCheoSSZvrcrdj3t6cZ3suA==" + }, + "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 + }, + "browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "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" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "builder-util-runtime": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.0.0.tgz", + "integrity": "sha512-SkpEtSmTkREDHRJnxKEv43aAYp8sYWY8fxYBhGLBLOBIRXeaIp6Kv3lBgSD7uR8jQtC7CA659sqJrpSV6zNvSA==", + "requires": { + "debug": "^4.3.2", + "sax": "^1.2.4" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz", + "integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "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==", + "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==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001325", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", + "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "requires": {} + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "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" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "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": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.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=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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": { + "map-visit": "^1.0.0", + "object-visit": "^1.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==", + "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==" + }, + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "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==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "conf": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.1.2.tgz", + "integrity": "sha512-o9Fv1Mv+6A0JpoayQ8JleNp3hhkbOJP/Re/Q+QqxMPHPkABVsRjQGWZn9A5GcqLiTNC6d89p2PB5ZhHVDSMwyg==", + "requires": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + } + }, + "dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "requires": { + "is-obj": "^2.0.0" + } + } + } + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + } + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "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" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", + "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", + "dev": true, + "requires": { + "fast-glob": "^3.2.5", + "glob-parent": "^6.0.0", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "core-js": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "dev": true + }, + "core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "critters": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", + "integrity": "sha512-ujxKtKc/mWpjrOKeaACTaQ1aP0O31M0ZPWhfl85jZF1smPU4Ivb9va5Ox2poif4zVJQQo0LCFlzGtEZAsCAPcw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.1.3", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "requires": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + } + } + }, + "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": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.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 + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "css-declaration-sorter": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true, + "requires": {} + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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 + } + } + }, + "css-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.2.0.tgz", + "integrity": "sha512-/rvHfYRjIpymZblf49w8jYcRo2y9gj6rV8UroHGmBxKrIyGLokpycyKzp9OkitvqT29ZSpzJ0Ic7SpnJX3sC8g==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-B3I5e17RwvKPJwsxjjWcdgpU/zqylzK1bPVghcmpFHRL48DXiBgrtqz1BJsn68+t/zzaLp9kYAaEDvQ7GyanFQ==", + "dev": true, + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "p-limit": "^3.0.2", + "postcss": "^8.3.5", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.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 + } + } + }, + "css-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", + "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", + "dev": true, + "requires": { + "css": "^2.0.0" + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "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 + } + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", + "dev": true, + "requires": { + "cssnano-preset-default": "^5.2.7", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^6.2.2", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.4", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.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 + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "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 + } + } + }, + "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": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "dev": true + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "requires": { + "mimic-fn": "^3.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" + } + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "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=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.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==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "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 + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "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==" + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "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==", + "devOptional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "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" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "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 + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "devOptional": true + }, + "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": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "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 + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "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" + } + }, + "duo_web_sdk": { + "version": "git+ssh://git@github.com/duosecurity/duo_web_sdk.git#378e855ce4a1de1d1b2f7fd60465e564b3e9fbda", + "from": "duo_web_sdk@git+https://github.com/duosecurity/duo_web_sdk.git" + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-16.1.0.tgz", + "integrity": "sha512-G4fpHmE3sgd497e0zEier/AmZ4fyReX8ozYAl468+FaI5kb44+69igRHQwRUtmPzv+fCn/Jm4wJQPfLe60WmUQ==", + "requires": { + "@electron/get": "^1.13.0", + "@types/node": "^14.6.2", + "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" + } + } + }, + "electron-log": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-4.4.6.tgz", + "integrity": "sha512-nirYgRdY+F+vclr8ijdwy2vW03IzFpDHTaKNWu76dEN21Y76+smcES5knS7cgHUUB0qNLOi8vZO36taakjbSXA==" + }, + "electron-store": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.1.tgz", + "integrity": "sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==", + "requires": { + "conf": "^10.0.3", + "type-fest": "^1.0.2" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" + } + } + }, + "electron-to-chromium": { + "version": "1.4.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", + "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "dev": true + }, + "electron-updater": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-5.0.0.tgz", + "integrity": "sha512-SC3sw92ewjrJFZIJamVOHqxW3yzFin/Q/Swf2FZodqm9xd4s8hCbPCfptpD/xBIcvQmAv2BAggbprwWq/fyp6w==", + "requires": { + "@types/semver": "^7.3.6", + "builder-util-runtime": "9.0.0", + "fs-extra": "^10.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "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==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "devOptional": 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==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true, + "peer": true + }, + "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": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-denodeify": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz", + "integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8=" + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "esbuild": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz", + "integrity": "sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.13.8", + "esbuild-darwin-64": "0.13.8", + "esbuild-darwin-arm64": "0.13.8", + "esbuild-freebsd-64": "0.13.8", + "esbuild-freebsd-arm64": "0.13.8", + "esbuild-linux-32": "0.13.8", + "esbuild-linux-64": "0.13.8", + "esbuild-linux-arm": "0.13.8", + "esbuild-linux-arm64": "0.13.8", + "esbuild-linux-mips64le": "0.13.8", + "esbuild-linux-ppc64le": "0.13.8", + "esbuild-netbsd-64": "0.13.8", + "esbuild-openbsd-64": "0.13.8", + "esbuild-sunos-64": "0.13.8", + "esbuild-windows-32": "0.13.8", + "esbuild-windows-64": "0.13.8", + "esbuild-windows-arm64": "0.13.8" + } + }, + "esbuild-android-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz", + "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz", + "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz", + "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz", + "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz", + "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz", + "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz", + "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz", + "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz", + "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz", + "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz", + "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.11.tgz", + "integrity": "sha512-DoThrkzunZ1nfRGoDN6REwmo8ZZWHd2ztniPVIR5RMw/Il9wiWEYBahb8jnMzQaSOxBsGp0PbyJeVLTUatnlcw==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz", + "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz", + "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz", + "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.13.8.tgz", + "integrity": "sha512-UbD+3nloiSpJWXTCInZQrqPe8Y+RLfDkY/5kEHiXsw/lmaEvibe69qTzQu16m5R9je/0bF7VYQ5jaEOq0z9lLA==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz", + "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz", + "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz", + "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "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-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "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==", + "devOptional": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "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" + } + } + } + }, + "eslint": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", + "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "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" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "dependencies": { + "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" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.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 + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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 + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "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" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "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 + }, + "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 + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "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 + } + } + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "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==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "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 + }, + "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 + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "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": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "fetch-cookie": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz", + "integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==", + "requires": { + "es6-denodeify": "^0.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "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=" + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": 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" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "dev": true + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "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" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "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==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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 + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "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 + }, + "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-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "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 + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + } + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "requires": { + "ini": "2.0.0" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "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 + }, + "globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.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==", + "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" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "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==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "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" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "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 + }, + "hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "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": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "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 + }, + "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==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-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-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "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" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "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 + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.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": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.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 + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "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": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, + "inquirer": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.2.tgz", + "integrity": "sha512-DHLKJwLPNgkfwNmsuEUKSejJFbkv0FMO9SMiQbjI3n5NQuCrSIBqP66ggqyz2a6t2qEolKrMjhQ3+W/xXgUQ+Q==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.3.0", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "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 + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.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-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "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-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "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" + }, + "dependencies": { + "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 + } + } + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.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" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "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-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "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 + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.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==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "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-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "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, + "requires": { + "is-docker": "^2.0.0" + } + }, + "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=" + }, + "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 + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "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": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "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": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dev": true, + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "dependencies": { + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dev": true, + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dev": true, + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "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, + "requires": {} + }, + "jest-preset-angular": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-11.1.1.tgz", + "integrity": "sha512-ZlYiKJhAQSU9wIjncX59xutcj49R4MiDsTPSwZiwdTAHQvHm32MS6SGimQIVBqh1DukfwYX0NXKS0D/onLAsLQ==", + "dev": true, + "requires": { + "bs-logger": "^0.2.6", + "esbuild": "0.14.11", + "esbuild-wasm": "0.14.11", + "jest-environment-jsdom": "^27.0.0", + "pretty-format": "^27.0.0", + "ts-jest": "^27.0.0" + }, + "dependencies": { + "esbuild": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.11.tgz", + "integrity": "sha512-xZvPtVj6yecnDeFb3KjjCM6i7B5TCAQZT77kkW/CpXTMnd6VLnRPKrUB1XHI1pSq6a4Zcy3BGueQ8VljqjDGCg==", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.14.11", + "esbuild-darwin-64": "0.14.11", + "esbuild-darwin-arm64": "0.14.11", + "esbuild-freebsd-64": "0.14.11", + "esbuild-freebsd-arm64": "0.14.11", + "esbuild-linux-32": "0.14.11", + "esbuild-linux-64": "0.14.11", + "esbuild-linux-arm": "0.14.11", + "esbuild-linux-arm64": "0.14.11", + "esbuild-linux-mips64le": "0.14.11", + "esbuild-linux-ppc64le": "0.14.11", + "esbuild-linux-s390x": "0.14.11", + "esbuild-netbsd-64": "0.14.11", + "esbuild-openbsd-64": "0.14.11", + "esbuild-sunos-64": "0.14.11", + "esbuild-windows-32": "0.14.11", + "esbuild-windows-64": "0.14.11", + "esbuild-windows-arm64": "0.14.11" + } + }, + "esbuild-android-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.11.tgz", + "integrity": "sha512-6iHjgvMnC/SzDH8TefL+/3lgCjYWwAd1LixYfmz/TBPbDQlxcuSkX0yiQgcJB9k+ibZ54yjVXziIwGdlc+6WNw==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.11.tgz", + "integrity": "sha512-olq84ikh6TiBcrs3FnM4eR5VPPlcJcdW8BnUz/lNoEWYifYQ+Po5DuYV1oz1CTFMw4k6bQIZl8T3yxL+ZT2uvQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.11.tgz", + "integrity": "sha512-Jj0ieWLREPBYr/TZJrb2GFH8PVzDqiQWavo1pOFFShrcmHWDBDrlDxPzEZ67NF/Un3t6sNNmeI1TUS/fe1xARg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.11.tgz", + "integrity": "sha512-C5sT3/XIztxxz/zwDjPRHyzj/NJFOnakAanXuyfLDwhwupKPd76/PPHHyJx6Po6NI6PomgVp/zi6GRB8PfrOTA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.11.tgz", + "integrity": "sha512-y3Llu4wbs0bk4cwjsdAtVOesXb6JkdfZDLKMt+v1U3tOEPBdSu6w8796VTksJgPfqvpX22JmPLClls0h5p+L9w==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.11.tgz", + "integrity": "sha512-Cg3nVsxArjyLke9EuwictFF3Sva+UlDTwHIuIyx8qpxRYAOUTmxr2LzYrhHyTcGOleLGXUXYsnUVwKqnKAgkcg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.11.tgz", + "integrity": "sha512-oeR6dIrrojr8DKVrxtH3xl4eencmjsgI6kPkDCRIIFwv4p+K7ySviM85K66BN01oLjzthpUMvBVfWSJkBLeRbg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.11.tgz", + "integrity": "sha512-vcwskfD9g0tojux/ZaTJptJQU3a7YgTYsptK1y6LQ/rJmw7U5QJvboNawqM98Ca3ToYEucfCRGbl66OTNtp6KQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.11.tgz", + "integrity": "sha512-+e6ZCgTFQYZlmg2OqLkg1jHLYtkNDksxWDBWNtI4XG4WxuOCUErLqfEt9qWjvzK3XBcCzHImrajkUjO+rRkbMg==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.11.tgz", + "integrity": "sha512-Rrs99L+p54vepmXIb87xTG6ukrQv+CzrM8eoeR+r/OFL2Rg8RlyEtCeshXJ2+Q66MXZOgPJaokXJZb9snq28bw==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.11.tgz", + "integrity": "sha512-JyzziGAI0D30Vyzt0HDihp4s1IUtJ3ssV2zx9O/c+U/dhUHVP2TmlYjzCfCr2Q6mwXTeloDcLS4qkyvJtYptdQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.11.tgz", + "integrity": "sha512-12luoRQz+6eihKYh1zjrw0CBa2aw3twIiHV/FAfjh2NEBDgJQOY4WCEUEN+Rgon7xmLh4XUxCQjnwrvf8zhACw==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.11.tgz", + "integrity": "sha512-l18TZDjmvwW6cDeR4fmizNoxndyDHamGOOAenwI4SOJbzlJmwfr0jUgjbaXCUuYVOA964siw+Ix+A+bhALWg8Q==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.11.tgz", + "integrity": "sha512-bmYzDtwASBB8c+0/HVOAiE9diR7+8zLm/i3kEojUH2z0aIs6x/S4KiTuT5/0VKJ4zk69kXel1cNWlHBMkmavQg==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.11.tgz", + "integrity": "sha512-9e1R6hv0hiU+BkJI2edqUuWfXUbOP2Mox+Ijl/uY1vLLlSsunkrcADqD/4Rz+VCEDzw6ecscJM+uJqR2fRmEUg==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.11.tgz", + "integrity": "sha512-J1Ys5hMid8QgdY00OBvIolXgCQn1ARhYtxPnG6ESWNTty3ashtc4+As5nTrsErnv8ZGUcWZe4WzTP/DmEVX1UQ==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.11.tgz", + "integrity": "sha512-h9FmMskMuGeN/9G9+LlHPAoiQk9jlKDUn9yA0MpiGzwLa82E7r1b1u+h2a+InprbSnSLxDq/7p5YGtYVO85Mlg==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.11.tgz", + "integrity": "sha512-dZp7Krv13KpwKklt9/1vBFBMqxEQIO6ri7Azf8C+ob4zOegpJmha2XY9VVWP/OyQ0OWk6cEeIzMJwInRZrzBUQ==", + "dev": true, + "optional": true + } + } + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dev": true, + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dev": true, + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dev": true, + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dev": true, + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "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.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "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", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "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 + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" + }, + "less": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", + "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "less-loader": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.0.1.tgz", + "integrity": "sha512-Crln//HpW9M5CbtdfWm3IO66Cvx1WhZQvNybXgfB2dD/6Sav9ppw+IWqs/FQKPBFO4B6X0X28Z0WNznshgwUzA==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "license-webpack-plugin": { + "version": "2.3.20", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz", + "integrity": "sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + } + }, + "lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lint-staged": { + "version": "12.3.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.7.tgz", + "integrity": "sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true + }, + "supports-color": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", + "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", + "dev": true + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "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 + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "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 + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.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" + } + } + } + }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true + }, + "lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "requires": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "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" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + } + } + }, + "memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "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 + } + } + }, + "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", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "mini-css-extract-plugin": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.2.tgz", + "integrity": "sha512-ZmqShkn79D36uerdED+9qdo1ZYG8C1YsWvXu0UMJxurZnSdgz7gQKO2EGv8T55MhDqG3DYmGtizZNpM/UbTlcA==", + "dev": true, + "requires": { + "schema-utils": "^3.1.0" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "msgpack5": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", + "integrity": "sha512-zC1vkcliryc4JGlL6OfpHumSYUHWFGimSI+OgfRCjTFLmKA2/foR9rMTOhWiqfOrfxJOctrpWPvrppf8XynJxw==", + "requires": { + "bl": "^2.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.3.6", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "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 + }, + "needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "nodemon": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", + "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5", + "update-notifier": "^5.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "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" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "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 + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-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 + } + } + }, + "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" + } + } + } + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "devOptional": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + }, + "dependencies": { + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + } + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "papaparse": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "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" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "piscina": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.1.0.tgz", + "integrity": "sha512-KTW4sjsCD34MHrUbx9eAAbuUSpVj407hQSgk/6Epkg0pbRBmv4a3UX7Sr8wxm9xYqQLnsN4mFOjqGDzHAdgKQg==", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "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": { + "find-up": "^4.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-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": "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 + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "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==", + "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==", + "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==", + "requires": { + "p-limit": "^2.0.0" + } + } + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + }, + "dependencies": { + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "dev": true, + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "dev": true, + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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 + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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 + } + } + }, + "postcss-discard-comments": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", + "dev": true, + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "dev": true, + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", + "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "dev": true, + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-loader": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.1.1.tgz", + "integrity": "sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-merge-longhand": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + } + }, + "postcss-merge-rules": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", + "dev": true, + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "dev": true, + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "dev": true, + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", + "dev": true + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "dev": true, + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "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 + } + } + }, + "postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-selector-not": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", + "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postinstall-build": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", + "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "optional": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "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 + } + } + }, + "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" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "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": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "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 + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "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 + } + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "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" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "optional": true + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.36.0.tgz", + "integrity": "sha512-fQzEjipfOv5kh930nu3Imzq3ie/sGDc/4KtQMJlt7RRdrkQSfe37Bwi/Rf/gfuYHsIuE1fIlDMvpyMcEwjnPvg==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", + "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "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" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + }, + "dependencies": { + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + } + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "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" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "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" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "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 + } + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "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 + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "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" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "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 + }, + "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 + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "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" + } + } + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "sockjs-client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "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 + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "source-map-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.0.tgz", + "integrity": "sha512-GKGWqWvYr04M7tn8dryIWvb0s8YM41z82iQv01yBtIylgxax0CwvSy6gc2Y02iuXwEfGWRlMicH0nvms9UZphw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "source-map-js": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "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", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "requires": { + "graceful-fs": "^4.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "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==" + } + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-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-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 + }, + "style-loader": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", + "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", + "dev": true, + "requires": {} + }, + "stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylus": { + "version": "0.54.8", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", + "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", + "dev": true, + "requires": { + "css-parse": "~2.0.0", + "debug": "~3.1.0", + "glob": "^7.1.6", + "mkdirp": "~1.0.4", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "semver": "^6.3.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "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": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.1.0.tgz", + "integrity": "sha512-qKO34QCsOtSJrXxQQmXsPeaVHh6hMumBAFIoJTcsSr2VzrA6o/CW9HCGR8spCjzJhN8oKQHdj/Ytx0wwXyElkw==", + "dev": true, + "requires": { + "fast-glob": "^3.2.5", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "requires": { + "debug": "^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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, + "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 + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "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" + } + }, + "terser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz", + "integrity": "sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA==", + "dev": true, + "requires": { + "jest-worker": "^27.0.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.0" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.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 + } + } + }, + "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-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tldjs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-2.3.1.tgz", + "integrity": "sha512-W/YVH/QczLUxVjnQhFC61Iq232NWu3TqDdO0S/MtXVz4xybejBov4ud+CIwN9aYqjOecEqIy0PscGkwpG9ZyTw==", + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "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", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "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" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-jest": { + "version": "27.1.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", + "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + } + }, + "ts-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + } + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "ttypescript": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", + "dev": true, + "requires": { + "resolve": ">=1.9.0" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typemoq": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", + "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "lodash": "^4.17.4", + "postinstall-build": "^5.0.1" + } + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, + "typescript-transform-paths": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-2.2.4.tgz", + "integrity": "sha512-i+/sgp3rw1ZronMCm2TKGBy1dlvN88Kd8CCb+HWnOE8+Hv0uIVnbC8xM5AD2t1JBCWabEhuH9p3n8DOVi0+R6g==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "dependencies": { + "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 + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "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.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "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 + }, + "webpack": { + "version": "5.71.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.71.0.tgz", + "integrity": "sha512-g4dFT7CFG8LY0iU5G8nBL6VlkT21Z7dcYDpJAEJV5Q1WLb9UwnFbrem1k7K52ILqEmomN7pnzWFxxE6SlDY56A==", + "dev": true, + "peer": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": 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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "requires": {} + }, + "enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true + } + } + }, + "webpack-dev-middleware": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.0.0.tgz", + "integrity": "sha512-9zng2Z60pm6A98YoRcA0wSxw1EYn7B7y5owX/Tckyt9KGyULTkLtiavjaXlWqOMkM0YtqGgL3PvMOFgyFLq8vw==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "mem": "^8.1.1", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "requires": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "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" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "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" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "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" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "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" + } + } + } + }, + "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": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "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" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "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" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "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" + } + } + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "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" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "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" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "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" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "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" + } + } + } + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "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" + } + }, + "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" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz", + "integrity": "sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw==", + "dev": true, + "requires": { + "webpack-sources": "^1.3.0" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.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" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "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 + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "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" + } + }, + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "dev": true, + "requires": {} + }, + "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 + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "zone.js": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", + "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + } +} diff --git a/jslib/package.json b/jslib/package.json new file mode 100644 index 00000000..96a3cc93 --- /dev/null +++ b/jslib/package.json @@ -0,0 +1,74 @@ +{ + "name": "@bitwarden/jslib", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/jslib" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist/**/*", + "lint": "eslint . && prettier --check .", + "lint:fix": "eslint . --fix", + "prettier": "prettier --write .", + "test": "jest", + "test:watch": "jest --watch", + "test:watch:all": "jest --watchAll", + "prepare": "husky install" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^12.2.13", + "@angular/compiler-cli": "^12.2.13", + "@angular/platform-browser-dynamic": "^12.2.13", + "@fluffy-spoon/substitute": "^1.202.0", + "@types/jest": "^27.4.1", + "@types/node": "^16.11.12", + "@typescript-eslint/eslint-plugin": "^5.10.1", + "@typescript-eslint/parser": "^5.10.1", + "commander": "7.2.0", + "concurrently": "^6.1.0", + "eslint": "^8.7.0", + "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "form-data": "4.0.0", + "husky": "^7.0.4", + "jest": "^27.5.1", + "jest-preset-angular": "^11.1.1", + "jsdom": "^16.5.3", + "lint-staged": "^12.1.2", + "node-forge": "^1.2.0", + "nodemon": "^2.0.7", + "prettier": "2.5.1", + "rimraf": "^3.0.2", + "rxjs": "^7.4.0", + "ts-loader": "^8.1.0", + "ts-node": "^10.4.0", + "tsconfig-paths": "^3.12.0", + "ttypescript": "^1.5.12", + "typemoq": "^2.1.0", + "typescript": "4.3.5", + "typescript-transform-paths": "^2.2.3", + "zone.js": "0.11.4" + }, + "dependencies": { + "@bitwarden/jslib-angular": "file:angular", + "@bitwarden/jslib-common": "file:common", + "@bitwarden/jslib-electron": "file:electron", + "@bitwarden/jslib-node": "file:node" + }, + "engines": { + "node": "~16", + "npm": "~8" + }, + "lint-staged": { + "*": "prettier --ignore-unknown --write", + "*.ts": "eslint --fix" + } +} diff --git a/jslib/shared/eslintrc.json b/jslib/shared/eslintrc.json new file mode 100644 index 00000000..70c61bc7 --- /dev/null +++ b/jslib/shared/eslintrc.json @@ -0,0 +1,29 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "prettier" + ], + "rules": { + "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled + "@typescript-eslint/no-unused-vars": ["warn", { "args": "none" }], + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ], + "@typescript-eslint/no-this-alias": [ + "error", + { + "allowedNames": ["self"] + } + ], + "no-console": "warn", + "import/no-unresolved": "off" // TODO: Look into turning off once each package is an actual package. + } +} diff --git a/jslib/shared/tsconfig.json b/jslib/shared/tsconfig.json new file mode 100644 index 00000000..3a70f6b9 --- /dev/null +++ b/jslib/shared/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "pretty": true, + "moduleResolution": "node", + "noImplicitAny": true, + "target": "ES6", + "module": "commonjs", + "lib": ["es5", "es6", "es7", "dom"], + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "outDir": "dist" + } +} diff --git a/jslib/tsconfig.json b/jslib/tsconfig.json new file mode 100644 index 00000000..63b3da4e --- /dev/null +++ b/jslib/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "pretty": true, + "moduleResolution": "node", + "noImplicitAny": true, + "target": "ES6", + "module": "commonjs", + "lib": ["es5", "es6", "es7", "dom"], + "sourceMap": true, + "declaration": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "declarationDir": "dist/types", + "outDir": "dist", + "baseUrl": ".", + "paths": { + "jslib-common/*": ["common/src/*"], + "jslib-angular/*": ["angular/src/*"], + "jslib-electron/*": ["electron/src/*"], + "jslib-node/*": ["node/src/*"] + }, + "plugins": [ + { + "transform": "typescript-transform-paths" + } + ] + }, + "exclude": ["node_modules", "dist"] +} diff --git a/jslib/tsconfig.spec.json b/jslib/tsconfig.spec.json new file mode 100644 index 00000000..fc8520e7 --- /dev/null +++ b/jslib/tsconfig.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 890aac20..7e520b70 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -6,7 +6,19 @@ import { FormsModule } from "@angular/forms"; import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { JslibModule } from "jslib-angular/jslib.module"; +import { CalloutComponent } from "jslib-angular/components/callout.component"; +import { IconComponent } from "jslib-angular/components/icon.component"; +import { BitwardenToastModule } from "jslib-angular/components/toastr.component"; +import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive"; +import { ApiActionDirective } from "jslib-angular/directives/api-action.directive"; +import { AutofocusDirective } from "jslib-angular/directives/autofocus.directive"; +import { BlurClickDirective } from "jslib-angular/directives/blur-click.directive"; +import { BoxRowDirective } from "jslib-angular/directives/box-row.directive"; +import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive"; +import { StopClickDirective } from "jslib-angular/directives/stop-click.directive"; +import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive"; +import { I18nPipe } from "jslib-angular/pipes/i18n.pipe"; +import { SearchCiphersPipe } from "jslib-angular/pipes/search-ciphers.pipe"; import { ApiKeyComponent } from "./accounts/apiKey.component"; import { EnvironmentComponent } from "./accounts/environment.component"; @@ -18,23 +30,38 @@ import { MoreComponent } from "./tabs/more.component"; import { SettingsComponent } from "./tabs/settings.component"; import { TabsComponent } from "./tabs/tabs.component"; - @NgModule({ imports: [ - AppRoutingModule, - BrowserAnimationsModule, BrowserModule, + BrowserAnimationsModule, FormsModule, - JslibModule, + AppRoutingModule, ServicesModule, + BitwardenToastModule.forRoot({ + maxOpened: 5, + autoDismiss: true, + closeButton: true, + }), ], declarations: [ + A11yTitleDirective, + ApiActionDirective, ApiKeyComponent, AppComponent, + AutofocusDirective, + BlurClickDirective, + BoxRowDirective, + CalloutComponent, DashboardComponent, EnvironmentComponent, + FallbackSrcDirective, + I18nPipe, + IconComponent, MoreComponent, + SearchCiphersPipe, SettingsComponent, + StopClickDirective, + StopPropDirective, TabsComponent, ], providers: [], diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts index 192b0286..db41029b 100644 --- a/src/app/services/services.module.ts +++ b/src/app/services/services.module.ts @@ -1,4 +1,4 @@ -import { APP_INITIALIZER, Injector, NgModule } from "@angular/core"; +import { APP_INITIALIZER, NgModule } from "@angular/core"; import { JslibServicesModule } from "jslib-angular/services/jslib-services.module"; import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";