From d13518594d89b7bd3a17b8fc7e53470e4264d82b Mon Sep 17 00:00:00 2001 From: Bastien Wirtz Date: Mon, 18 Oct 2021 09:20:51 -0700 Subject: [PATCH] Initial commit --- .eslintignore | 2 + .eslintrc.json | 87 + .github/ISSUE_TEMPLATE.md | 26 + .github/PULL_REQUEST_TEMPLATE.md | 18 + .github/workflows/integration.yml | 32 + .github/workflows/release.yml | 38 + .gitignore | 148 + CODE_OF_CONDUCT.md | 132 + LICENSE | 21 + README.md | 395 + bower.json | 9 + doc/assets/ic-battery_active_black.svg | 12 + .../ic-nav_android_back_active_black.svg | 12 + .../ic-nav_android_home_active_black.svg | 12 + .../ic-nav_android_multiapp_active_black.svg | 12 + doc/assets/ic-resolution_active_black.svg | 12 + doc/assets/ic-screencast_disabled.svg | 7 + doc/assets/ic-screenshot_active_black.svg | 22 + doc/assets/ic-screenshot_disabled.svg | 9 + doc/assets/ic_baseband_active_black.svg | 12 + doc/assets/ic_camera_active_black.svg | 15 + doc/assets/ic_clipboard_active_black.svg | 3 + doc/assets/ic_cloud_upload_active_black.svg | 12 + doc/assets/ic_diskIO_active_black.svg | 34 + doc/assets/ic_fullscreen_active_black.svg | 39 + .../ic_fullscreen_exit_active_black.svg | 39 + doc/assets/ic_icon_power_inactive_black.svg | 12 + doc/assets/ic_id_active_black.svg | 23 + doc/assets/ic_location_active_black.svg | 12 + doc/assets/ic_network_active_black.svg | 12 + doc/assets/ic_rotation_active_black.svg | 14 + doc/assets/ic_sound_active_black.svg | 14 + doc/assets/ic_textandcall_active_black.svg | 15 + doc/assets/screenshot.png | Bin 0 -> 182752 bytes gulp/graspify-squery.js | 46 + gulp/gulp-template-collector.js | 53 + gulpfile.js | 212 + package.json | 73 + src/GenymotionInstance.js | 830 ++ src/GenymotionManager.js | 234 + src/assets/images/hq-over.png | Bin 0 -> 1300 bytes src/assets/images/hq.png | Bin 0 -> 937 bytes src/assets/images/ic-battery_active.svg | 12 + src/assets/images/ic-battery_active_black.svg | 12 + src/assets/images/ic-battery_empty.svg | 34 + src/assets/images/ic-battery_inactive.svg | 12 + src/assets/images/ic-close-popup-default.svg | 10 + src/assets/images/ic-close-popup-hover.svg | 10 + .../images/ic-nav_android_back_active.svg | 12 + .../ic-nav_android_back_active_black.svg | 12 + .../images/ic-nav_android_back_inactive.svg | 12 + .../images/ic-nav_android_home_active.svg | 12 + .../ic-nav_android_home_active_black.svg | 12 + .../images/ic-nav_android_home_inactive.svg | 12 + .../images/ic-nav_android_multiapp_active.svg | 12 + .../ic-nav_android_multiapp_active_black.svg | 12 + .../ic-nav_android_multiapp_inactive.svg | 12 + src/assets/images/ic-resolution_active.svg | 12 + .../images/ic-resolution_active_black.svg | 12 + src/assets/images/ic-resolution_inactive.svg | 12 + src/assets/images/ic-screencast_default.svg | 7 + src/assets/images/ic-screencast_disabled.svg | 7 + src/assets/images/ic-screenshot_active.svg | 22 + .../images/ic-screenshot_active_black.svg | 22 + src/assets/images/ic-screenshot_default.svg | 9 + src/assets/images/ic-screenshot_disabled.svg | 9 + src/assets/images/ic-screenshot_inactive.svg | 22 + src/assets/images/ic_arrow_rotation_left.svg | 14 + src/assets/images/ic_arrow_rotation_right.svg | 14 + src/assets/images/ic_baseband_active.svg | 12 + .../images/ic_baseband_active_black.svg | 12 + src/assets/images/ic_baseband_inactive.svg | 12 + src/assets/images/ic_camera_active.svg | 15 + src/assets/images/ic_camera_active_black.svg | 15 + src/assets/images/ic_camera_inactive.svg | 15 + .../images/ic_click_to_display_default.svg | 85 + .../images/ic_click_to_display_hover.svg | 85 + src/assets/images/ic_clipboard_active.svg | 3 + src/assets/images/ic_clipboard_inactive.svg | 3 + src/assets/images/ic_cloud_upload.svg | 12 + .../images/ic_cloud_upload_congralutation.svg | 25 + src/assets/images/ic_cloud_upload_failed.svg | 23 + src/assets/images/ic_diskIO_active.svg | 34 + src/assets/images/ic_diskIO_active_black.svg | 34 + src/assets/images/ic_diskIO_inactive.svg | 34 + src/assets/images/ic_error_default.svg | 1 + src/assets/images/ic_fullscreen_active.svg | 39 + .../images/ic_fullscreen_active_black.svg | 39 + .../images/ic_fullscreen_exit_active.svg | 39 + .../ic_fullscreen_exit_active_black.svg | 39 + .../images/ic_fullscreen_exit_inactive.svg | 51 + src/assets/images/ic_fullscreen_inactive.svg | 39 + src/assets/images/ic_icon_mute.svg | 1 + src/assets/images/ic_icon_power_active.svg | 12 + src/assets/images/ic_icon_power_inactive.svg | 12 + .../images/ic_icon_power_inactive_black.svg | 12 + src/assets/images/ic_id_active.svg | 23 + src/assets/images/ic_id_active_black.svg | 23 + src/assets/images/ic_id_inactive.svg | 23 + src/assets/images/ic_installation_active.svg | 12 + .../images/ic_installation_active_black.svg | 12 + .../images/ic_installation_inactive.svg | 12 + src/assets/images/ic_location_active.svg | 12 + .../images/ic_location_active_black.svg | 12 + src/assets/images/ic_location_inactive.svg | 12 + src/assets/images/ic_more_active.svg | 15 + src/assets/images/ic_more_active_black.svg | 15 + src/assets/images/ic_more_inactive.svg | 15 + src/assets/images/ic_network_active.svg | 12 + src/assets/images/ic_network_active_black.svg | 12 + src/assets/images/ic_network_inactive.svg | 12 + src/assets/images/ic_pixelperfect_active.svg | 18 + .../images/ic_pixelperfect_active_black.svg | 18 + .../images/ic_pixelperfect_inactive.svg | 18 + src/assets/images/ic_rotation_active.svg | 14 + .../images/ic_rotation_active_black.svg | 14 + src/assets/images/ic_rotation_inactive.svg | 14 + src/assets/images/ic_sound_active.svg | 14 + src/assets/images/ic_sound_active_black.svg | 14 + src/assets/images/ic_sound_down_active.svg | 15 + src/assets/images/ic_sound_down_inactive.svg | 15 + src/assets/images/ic_sound_inactive.svg | 14 + src/assets/images/ic_sound_up_active.svg | 16 + src/assets/images/ic_sound_up_inactive.svg | 16 + src/assets/images/ic_textandcall_active.svg | 15 + .../images/ic_textandcall_active_black.svg | 15 + src/assets/images/ic_textandcall_inactive.svg | 13 + src/assets/images/ic_warning.svg | 1 + src/assets/images/loader.svg | 14 + src/assets/images/spinner-material.svg | 1 + src/plugins/Battery.js | 280 + src/plugins/ButtonsEvents.js | 230 + src/plugins/Camera.js | 127 + src/plugins/Clipboard.js | 180 + src/plugins/CoordinateUtils.js | 165 + src/plugins/FileUpload.js | 777 ++ src/plugins/Fullscreen.js | 131 + src/plugins/GPS.js | 578 + src/plugins/IOThrottling.js | 304 + src/plugins/Identifiers.js | 310 + src/plugins/KeyboardEvents.js | 228 + src/plugins/MouseEvents.js | 163 + src/plugins/MultiTouchEvents.js | 93 + src/plugins/Network.js | 475 + src/plugins/PeerConnectionStats.js | 110 + src/plugins/Phone.js | 222 + src/plugins/Screencast.js | 376 + src/plugins/StreamBitrate.js | 59 + src/plugins/StreamResolution.js | 63 + src/plugins/util/OverlayPlugin.js | 94 + src/plugins/util/iothrottling-profiles.js | 27 + src/plugins/util/network-profiles.js | 294 + src/scss/base/_genymotion.scss | 119 + src/scss/base/_variables.scss | 19 + src/scss/components/_battery.scss | 79 + src/scss/components/_clipboard.scss | 19 + src/scss/components/_fullscreen.scss | 36 + src/scss/components/_gps.scss | 50 + src/scss/components/_imei.scss | 30 + src/scss/components/_iothrottling.scss | 57 + src/scss/components/_network.scss | 60 + src/scss/components/_phone.scss | 19 + src/scss/components/_screencast.scss | 90 + src/scss/components/_toolbar.scss | 186 + src/scss/components/_turn.scss | 49 + src/scss/components/_upload.scss | 209 + src/scss/components/_uploader.scss | 26 + src/scss/components/_widgetwindow.scss | 167 + src/scss/main.scss | 22 + src/templates/bootstrap/dom.html | 14 + src/templates/bootstrap/script.js | 0 src/templates/bootstrap/style.css | 36 + src/templates/fullscreen/dom.html | 18 + src/templates/fullscreen/script.js | 0 src/templates/fullscreen/style.css | 41 + src/templates/fullwindow/dom.html | 12 + src/templates/fullwindow/script.js | 0 src/templates/fullwindow/style.css | 31 + src/templates/player/dom.html | 15 + src/templates/player/script.js | 0 src/templates/player/style.css | 23 + src/templates/player_minimal/dom.html | 12 + src/templates/player_minimal/script.js | 0 src/templates/player_minimal/style.css | 41 + src/templates/player_no_toolbar/dom.html | 7 + src/templates/player_no_toolbar/script.js | 0 src/templates/player_no_toolbar/style.css | 23 + src/templates/player_partial/dom.html | 12 + src/templates/player_partial/script.js | 0 src/templates/player_partial/style.css | 74 + src/worker/FileUploaderWorker.js | 146 + tests/config.js | 9 + tests/mocks/GenymotionInstance.js | 68 + tests/unit/battery.test.js | 181 + tests/unit/buttonsevent.test.js | 236 + tests/unit/camera.test.js | 105 + tests/unit/clipboard.test.js | 110 + tests/unit/fileupload.test.js | 293 + tests/unit/fullscreen.test.js | 25 + tests/unit/gps.test.js | 352 + tests/unit/identifiers.test.js | 185 + tests/unit/iothrottling.test.js | 155 + tests/unit/network.test.js | 236 + tests/unit/phone.test.js | 92 + tests/unit/streambitrate.test.js | 62 + yarn.lock | 9535 +++++++++++++++++ 206 files changed, 22926 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/integration.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bower.json create mode 100644 doc/assets/ic-battery_active_black.svg create mode 100644 doc/assets/ic-nav_android_back_active_black.svg create mode 100644 doc/assets/ic-nav_android_home_active_black.svg create mode 100644 doc/assets/ic-nav_android_multiapp_active_black.svg create mode 100644 doc/assets/ic-resolution_active_black.svg create mode 100644 doc/assets/ic-screencast_disabled.svg create mode 100644 doc/assets/ic-screenshot_active_black.svg create mode 100644 doc/assets/ic-screenshot_disabled.svg create mode 100644 doc/assets/ic_baseband_active_black.svg create mode 100644 doc/assets/ic_camera_active_black.svg create mode 100644 doc/assets/ic_clipboard_active_black.svg create mode 100644 doc/assets/ic_cloud_upload_active_black.svg create mode 100644 doc/assets/ic_diskIO_active_black.svg create mode 100644 doc/assets/ic_fullscreen_active_black.svg create mode 100644 doc/assets/ic_fullscreen_exit_active_black.svg create mode 100644 doc/assets/ic_icon_power_inactive_black.svg create mode 100644 doc/assets/ic_id_active_black.svg create mode 100644 doc/assets/ic_location_active_black.svg create mode 100644 doc/assets/ic_network_active_black.svg create mode 100644 doc/assets/ic_rotation_active_black.svg create mode 100644 doc/assets/ic_sound_active_black.svg create mode 100644 doc/assets/ic_textandcall_active_black.svg create mode 100644 doc/assets/screenshot.png create mode 100644 gulp/graspify-squery.js create mode 100644 gulp/gulp-template-collector.js create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/GenymotionInstance.js create mode 100644 src/GenymotionManager.js create mode 100644 src/assets/images/hq-over.png create mode 100644 src/assets/images/hq.png create mode 100644 src/assets/images/ic-battery_active.svg create mode 100644 src/assets/images/ic-battery_active_black.svg create mode 100644 src/assets/images/ic-battery_empty.svg create mode 100644 src/assets/images/ic-battery_inactive.svg create mode 100644 src/assets/images/ic-close-popup-default.svg create mode 100644 src/assets/images/ic-close-popup-hover.svg create mode 100644 src/assets/images/ic-nav_android_back_active.svg create mode 100644 src/assets/images/ic-nav_android_back_active_black.svg create mode 100644 src/assets/images/ic-nav_android_back_inactive.svg create mode 100644 src/assets/images/ic-nav_android_home_active.svg create mode 100644 src/assets/images/ic-nav_android_home_active_black.svg create mode 100644 src/assets/images/ic-nav_android_home_inactive.svg create mode 100644 src/assets/images/ic-nav_android_multiapp_active.svg create mode 100644 src/assets/images/ic-nav_android_multiapp_active_black.svg create mode 100644 src/assets/images/ic-nav_android_multiapp_inactive.svg create mode 100644 src/assets/images/ic-resolution_active.svg create mode 100644 src/assets/images/ic-resolution_active_black.svg create mode 100644 src/assets/images/ic-resolution_inactive.svg create mode 100644 src/assets/images/ic-screencast_default.svg create mode 100644 src/assets/images/ic-screencast_disabled.svg create mode 100644 src/assets/images/ic-screenshot_active.svg create mode 100644 src/assets/images/ic-screenshot_active_black.svg create mode 100644 src/assets/images/ic-screenshot_default.svg create mode 100644 src/assets/images/ic-screenshot_disabled.svg create mode 100644 src/assets/images/ic-screenshot_inactive.svg create mode 100644 src/assets/images/ic_arrow_rotation_left.svg create mode 100644 src/assets/images/ic_arrow_rotation_right.svg create mode 100644 src/assets/images/ic_baseband_active.svg create mode 100644 src/assets/images/ic_baseband_active_black.svg create mode 100644 src/assets/images/ic_baseband_inactive.svg create mode 100644 src/assets/images/ic_camera_active.svg create mode 100644 src/assets/images/ic_camera_active_black.svg create mode 100644 src/assets/images/ic_camera_inactive.svg create mode 100644 src/assets/images/ic_click_to_display_default.svg create mode 100644 src/assets/images/ic_click_to_display_hover.svg create mode 100644 src/assets/images/ic_clipboard_active.svg create mode 100644 src/assets/images/ic_clipboard_inactive.svg create mode 100644 src/assets/images/ic_cloud_upload.svg create mode 100644 src/assets/images/ic_cloud_upload_congralutation.svg create mode 100644 src/assets/images/ic_cloud_upload_failed.svg create mode 100644 src/assets/images/ic_diskIO_active.svg create mode 100644 src/assets/images/ic_diskIO_active_black.svg create mode 100644 src/assets/images/ic_diskIO_inactive.svg create mode 100644 src/assets/images/ic_error_default.svg create mode 100644 src/assets/images/ic_fullscreen_active.svg create mode 100644 src/assets/images/ic_fullscreen_active_black.svg create mode 100644 src/assets/images/ic_fullscreen_exit_active.svg create mode 100644 src/assets/images/ic_fullscreen_exit_active_black.svg create mode 100644 src/assets/images/ic_fullscreen_exit_inactive.svg create mode 100644 src/assets/images/ic_fullscreen_inactive.svg create mode 100644 src/assets/images/ic_icon_mute.svg create mode 100644 src/assets/images/ic_icon_power_active.svg create mode 100644 src/assets/images/ic_icon_power_inactive.svg create mode 100644 src/assets/images/ic_icon_power_inactive_black.svg create mode 100644 src/assets/images/ic_id_active.svg create mode 100644 src/assets/images/ic_id_active_black.svg create mode 100644 src/assets/images/ic_id_inactive.svg create mode 100644 src/assets/images/ic_installation_active.svg create mode 100644 src/assets/images/ic_installation_active_black.svg create mode 100644 src/assets/images/ic_installation_inactive.svg create mode 100644 src/assets/images/ic_location_active.svg create mode 100644 src/assets/images/ic_location_active_black.svg create mode 100644 src/assets/images/ic_location_inactive.svg create mode 100644 src/assets/images/ic_more_active.svg create mode 100644 src/assets/images/ic_more_active_black.svg create mode 100644 src/assets/images/ic_more_inactive.svg create mode 100644 src/assets/images/ic_network_active.svg create mode 100644 src/assets/images/ic_network_active_black.svg create mode 100644 src/assets/images/ic_network_inactive.svg create mode 100644 src/assets/images/ic_pixelperfect_active.svg create mode 100644 src/assets/images/ic_pixelperfect_active_black.svg create mode 100644 src/assets/images/ic_pixelperfect_inactive.svg create mode 100644 src/assets/images/ic_rotation_active.svg create mode 100644 src/assets/images/ic_rotation_active_black.svg create mode 100644 src/assets/images/ic_rotation_inactive.svg create mode 100644 src/assets/images/ic_sound_active.svg create mode 100644 src/assets/images/ic_sound_active_black.svg create mode 100644 src/assets/images/ic_sound_down_active.svg create mode 100644 src/assets/images/ic_sound_down_inactive.svg create mode 100644 src/assets/images/ic_sound_inactive.svg create mode 100644 src/assets/images/ic_sound_up_active.svg create mode 100644 src/assets/images/ic_sound_up_inactive.svg create mode 100644 src/assets/images/ic_textandcall_active.svg create mode 100644 src/assets/images/ic_textandcall_active_black.svg create mode 100644 src/assets/images/ic_textandcall_inactive.svg create mode 100644 src/assets/images/ic_warning.svg create mode 100644 src/assets/images/loader.svg create mode 100644 src/assets/images/spinner-material.svg create mode 100644 src/plugins/Battery.js create mode 100644 src/plugins/ButtonsEvents.js create mode 100644 src/plugins/Camera.js create mode 100644 src/plugins/Clipboard.js create mode 100644 src/plugins/CoordinateUtils.js create mode 100644 src/plugins/FileUpload.js create mode 100644 src/plugins/Fullscreen.js create mode 100644 src/plugins/GPS.js create mode 100644 src/plugins/IOThrottling.js create mode 100644 src/plugins/Identifiers.js create mode 100644 src/plugins/KeyboardEvents.js create mode 100644 src/plugins/MouseEvents.js create mode 100644 src/plugins/MultiTouchEvents.js create mode 100644 src/plugins/Network.js create mode 100644 src/plugins/PeerConnectionStats.js create mode 100644 src/plugins/Phone.js create mode 100644 src/plugins/Screencast.js create mode 100644 src/plugins/StreamBitrate.js create mode 100644 src/plugins/StreamResolution.js create mode 100644 src/plugins/util/OverlayPlugin.js create mode 100644 src/plugins/util/iothrottling-profiles.js create mode 100644 src/plugins/util/network-profiles.js create mode 100644 src/scss/base/_genymotion.scss create mode 100644 src/scss/base/_variables.scss create mode 100644 src/scss/components/_battery.scss create mode 100644 src/scss/components/_clipboard.scss create mode 100644 src/scss/components/_fullscreen.scss create mode 100644 src/scss/components/_gps.scss create mode 100644 src/scss/components/_imei.scss create mode 100644 src/scss/components/_iothrottling.scss create mode 100644 src/scss/components/_network.scss create mode 100644 src/scss/components/_phone.scss create mode 100644 src/scss/components/_screencast.scss create mode 100644 src/scss/components/_toolbar.scss create mode 100644 src/scss/components/_turn.scss create mode 100644 src/scss/components/_upload.scss create mode 100644 src/scss/components/_uploader.scss create mode 100644 src/scss/components/_widgetwindow.scss create mode 100644 src/scss/main.scss create mode 100644 src/templates/bootstrap/dom.html create mode 100644 src/templates/bootstrap/script.js create mode 100644 src/templates/bootstrap/style.css create mode 100644 src/templates/fullscreen/dom.html create mode 100644 src/templates/fullscreen/script.js create mode 100644 src/templates/fullscreen/style.css create mode 100644 src/templates/fullwindow/dom.html create mode 100644 src/templates/fullwindow/script.js create mode 100644 src/templates/fullwindow/style.css create mode 100644 src/templates/player/dom.html create mode 100644 src/templates/player/script.js create mode 100644 src/templates/player/style.css create mode 100644 src/templates/player_minimal/dom.html create mode 100644 src/templates/player_minimal/script.js create mode 100644 src/templates/player_minimal/style.css create mode 100644 src/templates/player_no_toolbar/dom.html create mode 100644 src/templates/player_no_toolbar/script.js create mode 100644 src/templates/player_no_toolbar/style.css create mode 100644 src/templates/player_partial/dom.html create mode 100644 src/templates/player_partial/script.js create mode 100644 src/templates/player_partial/style.css create mode 100644 src/worker/FileUploaderWorker.js create mode 100644 tests/config.js create mode 100644 tests/mocks/GenymotionInstance.js create mode 100644 tests/unit/battery.test.js create mode 100644 tests/unit/buttonsevent.test.js create mode 100644 tests/unit/camera.test.js create mode 100644 tests/unit/clipboard.test.js create mode 100644 tests/unit/fileupload.test.js create mode 100644 tests/unit/fullscreen.test.js create mode 100644 tests/unit/gps.test.js create mode 100644 tests/unit/identifiers.test.js create mode 100644 tests/unit/iothrottling.test.js create mode 100644 tests/unit/network.test.js create mode 100644 tests/unit/phone.test.js create mode 100644 tests/unit/streambitrate.test.js create mode 100644 yarn.lock diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..fc1adf3a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +test/reports/* +dist diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..07ba798d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,87 @@ +{ + "extends": "eslint:recommended", + "env": { + "browser": true, + "es6": true, + "node": true, + "jest": true + }, + "parserOptions": { + "ecmaVersion": 6 + }, + "rules": { + // Possible errors + "no-console": "error", + "no-extra-parens": "error", + "no-loss-of-precision": "error", + "no-promise-executor-return": "error", + "no-template-curly-in-string": "error", + "no-unreachable-loop": "error", + "require-atomic-updates": "error", + "no-useless-backreference": "error", + // Best practices + "array-callback-return": ["error", { "checkForEach": true }], + "block-scoped-var": "error", + "consistent-return": "error", + "curly": "error", + "default-case": "error", + "default-case-last": "error", + "default-param-last": "error", + "dot-notation": "error", + "eqeqeq": "error", + "no-constructor-return": "error", + "no-else-return": "error", + "no-extra-bind": "error", + "no-multi-spaces" : "error", + "no-underscore-dangle": "error", + "no-sequences": "error", + "no-unused-expressions": "warn", + "prefer-regex-literals": "warn", + "valid-jsdoc": ["error", {"requireReturn": false}], + // Strict mode + "strict": "error", + // Variables + "no-shadow": "error", + "no-undefined": "error", + "no-use-before-define": "error", + // Stylistic issues + "array-bracket-newline": ["error", "consistent"], + "array-bracket-spacing": ["error", "never"], + "array-element-newline": ["error", "consistent"], + "block-spacing": "error", + "brace-style": "error", + "camelcase": ["error", {"ignoreDestructuring": true, "ignoreGlobals": true}], + "comma-dangle": ["error", "only-multiline"], + "computed-property-spacing": ["error", "never", { "enforceForClassMembers": true }], + "consistent-this": ["error", "self"], + "eol-last": ["error", "always"], + "func-call-spacing": ["error", "never"], + "func-style": ["error", "declaration", { "allowArrowFunctions": true }], + "indent": ["error", 4], + "keyword-spacing": "error", + "linebreak-style": ["error", "unix"], + "max-len": ["error", 120, {"ignoreComments": true}], + "multiline-comment-style": ["error", "starred-block"], + "new-cap": ["error", { "properties": false }], + "new-parens": "error", + "newline-per-chained-call": "error", + "no-multi-assign": "error", + "no-multiple-empty-lines": ["error", { "max": 1 } ], + "no-trailing-spaces": "error", + "object-curly-spacing": ["error", "never"], + "padded-blocks": ["error", "never"], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "semi-style": ["error", "last"], + "space-before-function-paren": ["error", "never"], + "space-in-parens": ["error", "never"], + "spaced-comment": ["error", "always"], + // ECMAScript 6 + "arrow-parens": ["error", "always"], + "arrow-spacing": "error", + "no-confusing-arrow": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-spread": "error" + } +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..fcff4a41 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,26 @@ +## Prerequisites + +- [ ] I've read & comply with the [contributing guidelines](https://github.com/Genymobile/genymotion-device-web-player/blob/main/CONTRIBUTING.md) +* [ ] I've searched open issues if my demand is already reported. + +## Description + +Description of the bug or feature / support request . + +## Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** + +What you expected to happen. + +**Actual behavior:** + +What actually happened. + +## Versions / environment + +If you are reporting a bug, please describe your environement and the versions you are using (you can get this information from executing `npm version`). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..f5e7bfb6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +## Description + +Please include a summary of the change and which issue is fixed if any. Please also include relevant motivation and context (a screenshot is appreciated). + +Fixes # + +## Type of change + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +## Checklist: + +- [ ] I've read & comply with the [contributing guidelines](https://github.com/Genymobile/genymotion-device-web-player/blob/main/CONTRIBUTING.md) +- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers. +- [ ] I have made corresponding changes to the documentation (README.md). +- [ ] I've checked my modifications for any breaking changes. diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml new file mode 100644 index 00000000..fb9767a5 --- /dev/null +++ b/.github/workflows/integration.yml @@ -0,0 +1,32 @@ +# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Integration + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: 'yarn' + - run: yarn install + - run: yarn validate + - run: yarn test + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..ac30a98a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +# This workflow will run lints & tests then create a GitHub release and publish a package to NPM +# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages + +name: Release + +on: + push: + tags: [v*] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - name: Build project + run: | + yarn install + yarn validate + yarn test + yarn build + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + - name: NPM Release + run: yarn publish --access public + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9e963e8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,148 @@ + +# Created by https://www.gitignore.io/api/node,jetbrains,linux,osx,virtualenv + +# Build artifact +/dist + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Documentation +docs/private +docs/public + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# test report directory +tests/reports + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +### Microsoft ### +# Visual Studio Code +.vscode/ + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +.idea/* +*.iml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml + + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### VirtualEnv ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + + +### Other ### +# Swap files +*.swp diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..81e1663b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement [here](https://www.genymotion.com/contact/). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0bc11bae --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Genymobile + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..028225e2 --- /dev/null +++ b/README.md @@ -0,0 +1,395 @@ +# Genymotion device web player + +![npm](https://img.shields.io/npm/v/@genymotion/device-web-player) +![GitHub](https://img.shields.io/github/license/Genymobile/genymotion-device-web-player) + + + +This repository contains the Genymotion device web player javascript SDK. +It provides an easy way to integrate **Genymotion devices** running in the cloud into any web application. You will be able to display an emulator screen and interact with the device. + +It focuses on: + + - **compatibility** (vanilla javascript, no external framework used) + - **performance** (30fps or more) + - **quality** (Up to 1920×1080) + - **low latency** + +For more information about Genymotion devices, please visit [genymotion website](https://www.genymotion.com). + +## Table of contents +1. [Requirements](#Requirements) +2. [Getting started](#getting-started) + 1. [With NPM/Yarn](#with-npmyarn) + 2. [With CDN](#with-cdn) +3. [Usage](#usage) +4. [Features & options](#features--options) + +## Requirements +A Modern, WebRTC compatible, Web browser: + - Chrome (60+) + - Firefox (58+) + - Safari (11+) + - Edge (15+) + - Opera (22+) + +## Getting started +### With NPM/Yarn + +Using yarn: +```bash +yarn add @genymotion/device-web-player +``` + +Using npm: +```bash +npm install @genymotion/device-web-player +``` + +Package import (commonJS): +```js +const GenymotionManager = require('genymotion/device-web-player'); +``` + +```html + +``` + +### With CDN + +```html + + +``` + +## Usage + +Use `GenymotionManager` to instanciate one or more Genymotion device player. +All you need is an HTML element to use as a container. See example below. + +```html +
+ + +``` + +## Features & options + +A device player instance can be configured using the `options` argument (object). Possible configuration key / value are described below. + +### `template` + +* **Type:** `String` +* **Default:** `player` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Defines the layout of the player. Can be one of the following: `bootstrap`, `fullscreen`, `fullwindow`, `player`, `player_minimal`, `player_no_toolbar`, `player_partial`. + +### `token` + +* **Type:** `String` +* **Default:** `undefined` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Instance access token, the shared secret used to connect to the device. + +### `i18n` + +* **Type:** `Object` +* **Default:** `{}` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Alternative translation for the player UI. + +### `stun` + +* **Type:** `Object` +* **Default:** `{}` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +WebRTC STUN servers configuration. Format: + +```js +{ + urls: [ + 'stun:stun-server1.org:80', + 'stun:stun-server2.org:443', + ... + ], +} +``` + +### `turn` + +* **Type:** `Object` +* **Default:** `{}` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +WebRTC TURN servers configuration. Format: +```js +{ + urls: [], + username: "myUsername", + credential: "myPassword", + default: false // Whether or not we should use the TURN servers by default. Default: false. +} +``` + +### `streamResolution` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `SaaS` +* **Details:** +Enables or disables the video stream quality widget. + +### `streamBitrate` + +* **Type:** `Boolean` +* **Default:** `false` +* **Compatibility:** `SaaS` +* **Details:** +Enables or disables the stream bitrate widget. + +### `touch` + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the touch events (fingers on screen). If you want to disable all VM interaction, please also disable `mouse` and `keyboard`. + +### `mouse` + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the mouse events. If you want to disable all VM interaction, please also disable `touch` and `keyboard`. + +### `keyboard` + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the keyboard widget. This widget can be used to transmit keyboard key strokes to the Android virtual device. + +### `volume` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the volume widget. This widget can be used to increase or decrease the volume of the Android virtual device. + +### `rotation` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the rotation widget. This widget can be used to rotate the Android virtual device. + +### `navbar` + +... +... +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the navbar widgets. This widget can be used to navigate in the Android virtual device like when using hardware buttons. + +### `power` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the power widget. This widget can be used to poweroff or reboot the Android virtual device. + +### `fullscreen` + +... +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the fullscreen widget. This widget can be used to make the player go fullscreen. + +### `camera` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the camera widget. This widget can be used to forward local webcam to the Android virtual device. + +### `fileUpload` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the fileUpload widget and drag & drop. This widget can be used to forward local file to the Android virtual device. When drag & dropping APK or ZIP files, it will install them. + +### `fileUploadUrl` + +* **Type:** `String` +* **Default:** `undefined` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Set the file upload url, required if `fileUpload` is set to `true`. + +### `clipboard` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the clipboard widget. This widget can be used to forward local clipboard to the Android virtual device. + +### `battery` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the battery widget. This widget can be used to set the battery level and state of the Android virtual device. + +### `gps` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the gps widget. This widget can be used to set the gps location of the Android virtual device. + +### `gpsSpeedSupport` + +* **Type:** `Boolean` +* **Default:** `false` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables gps speed support. + +### `capture` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the capture widget. This widget can be used to capture the screen of the Android virtual device (screenshot or screencast). + +### `identifiers` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** + +Enables or disables the identifiers widget. This widget can be used to set the identifiers (Android ID / IMEI) of the Android virtual device. + +### `network` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the network widget. This widget can be used to set the network (throttling and baseband) of the Android virtual device. + +### `phone` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** + +Enables or disables the phone widget. This widget can be used to send SMS or phone call the Android virtual device. + +### `diskIO` + +... + +* **Type:** `Boolean` +* **Default:** `true` +* **Compatibility:** `PaaS`, `SaaS` +* **Details:** +Enables or disables the diskIO widget. This widget can be used to modify Disk IO (throttling) of the Android virtual device. + + +### `translateHomeKey` + +* **Type:** `Boolean` +* **Default:** `false` +* **Compatibility:** `PaaS` +* **Details:** +Translate home key to `META` + `ENTER` + + +### `baseband` + +... + +* **Type:** `Boolean` +* **Default:** `false` +* **Compatibility:** `PaaS` +* **Details:** +Enable or disable baseband (MMC/MNC) widget + +### `connectionFailedURL` + +* **Type:** `String` +* **Default:** `undefined` +* **Compatibility:** `SaaS`, `PaaS` +* **Details:** +Redirection page in case of connection error. diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..cfea4849 --- /dev/null +++ b/bower.json @@ -0,0 +1,9 @@ +{ + "name": "genymotion-webrtc-client", + "main": [ + "dist/js/gm-player.min.js", + "dist/css/gm-player.css" + ], + "moduleType": "globals", + "version": "1.4.1" +} diff --git a/doc/assets/ic-battery_active_black.svg b/doc/assets/ic-battery_active_black.svg new file mode 100644 index 00000000..117093db --- /dev/null +++ b/doc/assets/ic-battery_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-battery_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-nav_android_back_active_black.svg b/doc/assets/ic-nav_android_back_active_black.svg new file mode 100644 index 00000000..81815590 --- /dev/null +++ b/doc/assets/ic-nav_android_back_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-nav_android_home_active_black.svg b/doc/assets/ic-nav_android_home_active_black.svg new file mode 100644 index 00000000..da1ff673 --- /dev/null +++ b/doc/assets/ic-nav_android_home_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_home_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-nav_android_multiapp_active_black.svg b/doc/assets/ic-nav_android_multiapp_active_black.svg new file mode 100644 index 00000000..dec8656e --- /dev/null +++ b/doc/assets/ic-nav_android_multiapp_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_multiapp_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-resolution_active_black.svg b/doc/assets/ic-resolution_active_black.svg new file mode 100644 index 00000000..fe85a92f --- /dev/null +++ b/doc/assets/ic-resolution_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-resolution_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-screencast_disabled.svg b/doc/assets/ic-screencast_disabled.svg new file mode 100644 index 00000000..bda60caa --- /dev/null +++ b/doc/assets/ic-screencast_disabled.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/doc/assets/ic-screenshot_active_black.svg b/doc/assets/ic-screenshot_active_black.svg new file mode 100644 index 00000000..316af4dd --- /dev/null +++ b/doc/assets/ic-screenshot_active_black.svg @@ -0,0 +1,22 @@ + + + + ic-screenshot_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic-screenshot_disabled.svg b/doc/assets/ic-screenshot_disabled.svg new file mode 100644 index 00000000..e970f66b --- /dev/null +++ b/doc/assets/ic-screenshot_disabled.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/doc/assets/ic_baseband_active_black.svg b/doc/assets/ic_baseband_active_black.svg new file mode 100644 index 00000000..c2ead57c --- /dev/null +++ b/doc/assets/ic_baseband_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_baseband_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_camera_active_black.svg b/doc/assets/ic_camera_active_black.svg new file mode 100644 index 00000000..3cea1253 --- /dev/null +++ b/doc/assets/ic_camera_active_black.svg @@ -0,0 +1,15 @@ + + + + ic_camera_active_black + Created with Sketch. + + + + + + + + + + diff --git a/doc/assets/ic_clipboard_active_black.svg b/doc/assets/ic_clipboard_active_black.svg new file mode 100644 index 00000000..a0c56024 --- /dev/null +++ b/doc/assets/ic_clipboard_active_black.svg @@ -0,0 +1,3 @@ + + + diff --git a/doc/assets/ic_cloud_upload_active_black.svg b/doc/assets/ic_cloud_upload_active_black.svg new file mode 100644 index 00000000..acb296fc --- /dev/null +++ b/doc/assets/ic_cloud_upload_active_black.svg @@ -0,0 +1,12 @@ + + + + +Group 5 +Created with Sketch. + + diff --git a/doc/assets/ic_diskIO_active_black.svg b/doc/assets/ic_diskIO_active_black.svg new file mode 100644 index 00000000..c2dd0bd1 --- /dev/null +++ b/doc/assets/ic_diskIO_active_black.svg @@ -0,0 +1,34 @@ + + + + ic_diskIO_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_fullscreen_active_black.svg b/doc/assets/ic_fullscreen_active_black.svg new file mode 100644 index 00000000..0f143fc8 --- /dev/null +++ b/doc/assets/ic_fullscreen_active_black.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_fullscreen_exit_active_black.svg b/doc/assets/ic_fullscreen_exit_active_black.svg new file mode 100644 index 00000000..3d6368f5 --- /dev/null +++ b/doc/assets/ic_fullscreen_exit_active_black.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_exit_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/assets/ic_icon_power_inactive_black.svg b/doc/assets/ic_icon_power_inactive_black.svg new file mode 100644 index 00000000..bab7ae55 --- /dev/null +++ b/doc/assets/ic_icon_power_inactive_black.svg @@ -0,0 +1,12 @@ + + + + ic_icon_power_inactive_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_id_active_black.svg b/doc/assets/ic_id_active_black.svg new file mode 100644 index 00000000..6bd534a2 --- /dev/null +++ b/doc/assets/ic_id_active_black.svg @@ -0,0 +1,23 @@ + + + + ic_id_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_location_active_black.svg b/doc/assets/ic_location_active_black.svg new file mode 100644 index 00000000..dcb3fd8e --- /dev/null +++ b/doc/assets/ic_location_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_location_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_network_active_black.svg b/doc/assets/ic_network_active_black.svg new file mode 100644 index 00000000..e44af977 --- /dev/null +++ b/doc/assets/ic_network_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_network_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_rotation_active_black.svg b/doc/assets/ic_rotation_active_black.svg new file mode 100644 index 00000000..f82037b9 --- /dev/null +++ b/doc/assets/ic_rotation_active_black.svg @@ -0,0 +1,14 @@ + + + + ic_rotation_active_black + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_sound_active_black.svg b/doc/assets/ic_sound_active_black.svg new file mode 100644 index 00000000..eb53593b --- /dev/null +++ b/doc/assets/ic_sound_active_black.svg @@ -0,0 +1,14 @@ + + + + ic_sound_active_black + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/ic_textandcall_active_black.svg b/doc/assets/ic_textandcall_active_black.svg new file mode 100644 index 00000000..cac61b47 --- /dev/null +++ b/doc/assets/ic_textandcall_active_black.svg @@ -0,0 +1,15 @@ + + + + ic_textandcall_active_black + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/doc/assets/screenshot.png b/doc/assets/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..95425705295539297eb0b025086855fe5527336c GIT binary patch literal 182752 zcmcG#1y@`_(=9xMI}9#^ySux)dvFhK!QI_mgS(Sp!6CQ>x8Uvs4|d6WpXdGVANXd? znwizBPoF;3-Br74?}<`Tl14_rM*si-$g(mLY5)MR6##%JgM;~KnYx>i`S^lz6_eF~ z`*``nnMVQuBmh|nQ4P=Bvo5br3vEs6Hv^xq&l}a+H~O+N7AUeKgJ?1oUr1?OIaY(^ zTB8qO5ol(FKOJG8hbIuPas4%MI?;NGd@T-ods)5siVMw}SeQlDCxV-dD+7l|r>>r+q6lB)-kn)>A!zD6n>9vUHI>G<9Wb}{V@MKPjV#boCctX^Zcii|2%nw!4LQC zrg5f$lZ9#^U~X=%?e9so^F{k1)6?10;a60^*zJE z*I>WaVj;ofS>Di zkhR?IN}Y*^WAFuJX=QbN;n72|WEAjfvalxm|5^UOKf&y67agZgzulHh#?u)eN2G;j zSN^k9N5-sM7ju3c$`8jvzYk@6%gW22Ac-FVmusyFuAb+%M*J%GFE`VNH`Bsj%gf8Z zy8rm!sgc95v$K0n`)oi0JO>Ci0+9*0kQjXq^E~Qw{+~_=Qd@63%x@6S50}Dp_U_65 z*#XkvZ1nY{@c-JyNrQyiN%~VTdF1tfo)#q@K|xu$c=Ph(P29-!|7;So;`Giv3SK(W zA-DpiG>G&A5IFkO{C1G=t)W2@=@@EIvmP)Jti?#wI{j?cYxiEua3yoe0nUGm=Mf6}wbaG@7!K+)UiO9WhD-GNVr+>fiRKLe z@yOx1C|P}4q*Mn4_5O`23}nXLg66@DOe!^~S|uI3PBerJ>Z1`S-UA%K1&T~To{3

CuaQ*a|4Q~_)tNGz&|hGZi3cMJ)2 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�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 CfQzaC literal 0 HcmV?d00001 diff --git a/gulp/graspify-squery.js b/gulp/graspify-squery.js new file mode 100644 index 00000000..4f4087a7 --- /dev/null +++ b/gulp/graspify-squery.js @@ -0,0 +1,46 @@ +'use strict'; + +const grasp = require('grasp'); +const transformTools = require('browserify-transform-tools'); + +const options = { + jsFilesOnly: true, +}; + +/** + * String transform is called per-module. + * In that, as many `require` calls you have, that many times it is called. + * If you replace `require` call, transform for that module won't be called. + * + * Transform can be called globally as `browserify -g graspify`. + * In that case transform is called for each nested module as well, + * getting global config, defined in package.json. + * + * `opts.config` is taken from package.json, can be whether object or array. + * `opts.opts` is taken as browserify transform param. + * + * Module taken from https://www.npmjs.com/package/graspify + */ +module.exports = transformTools.makeStringTransform('graspify', options, (content, opts, done) => { + try { + // Normalize plain replacements + if (opts.opts && typeof opts.opts[0] === 'string') { + opts.opts = [opts.opts]; + } + if (opts.config && typeof opts.config[0] === 'string') { + opts.config = [opts.config]; + } + + // Merge opts & config for the full list of replacements an loop over + [].concat(opts.opts || [], opts.config || []).forEach((args) => { + const selectorType = args.length === 3 ? args.shift() : 'squery'; + const selector = args[0]; + const replacement = args[1]; + content = grasp.replace(selectorType, selector, replacement, content); + }); + + done(null, content); + } catch (e) { + done(e); + } +}); diff --git a/gulp/gulp-template-collector.js b/gulp/gulp-template-collector.js new file mode 100644 index 00000000..4e7d8f0b --- /dev/null +++ b/gulp/gulp-template-collector.js @@ -0,0 +1,53 @@ +'use strict'; + +/** + * gulp/gulp-template-collector.js + * Gulp plugin to collect all template resources + */ + +const through = require('through2'); +const File = require('vinyl'); + +/** + * templateCollector + * Collect / combine all resources for an individual template + * + * @param {Object} options {Object} + * @param {string} options.file concat all templates into single file + * @return {Object} Gulp plugin + */ +module.exports = function templateCollector(options) { + const templates = {}; + options = options || {}; + options.file = options.file || 'templates.js'; + + return through.obj( + function transform(file, enc, done) { + // Ensure file, not directory + if (file.isNull()) { + return done(); + } + + // Break up filename + const pieces = file.relative.split('/'); + const template = pieces[0]; + const fileName = pieces[1]; + + // Capture contents + if (!templates[template]) { + templates[template] = {}; + } + templates[template][fileName.split('.')[1]] = file.contents.toString(); + + return done(); + }, + function flush(done) { + const templateBlob = JSON.stringify(templates); + this.push(new File({ + path: options.file, + contents: Buffer.from(templateBlob), + })); + done(); + } + ); +}; diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..86dfe5bb --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,212 @@ +'use strict'; + +/** + * gulpfile.js + * Gulp task definitions + */ + +// This variable is used to store templates generated from the template folder +let templates; + +const autoprefixer = require('gulp-autoprefixer'); +const babel = require('gulp-babel'); +const base64 = require('gulp-css-base64'); +const browserify = require('browserify'); +const browserSync = require('browser-sync').create(); +const concat = require('gulp-concat'); +const del = require('del'); +const graspify = require('./gulp/graspify-squery'); +const gulp = require('gulp'); +const gulpif = require('gulp-if'); +const inject = require('gulp-inject'); +const merge2 = require('merge2'); +const minifyCss = require('gulp-clean-css'); +const minifyHtml = require('gulp-htmlmin'); +const sass = require('gulp-sass'); +const source = require('vinyl-source-stream'); +const streamify = require('gulp-streamify'); +const tap = require('gulp-tap'); +const templateCollector = require('./gulp/gulp-template-collector'); +const uglify = require('gulp-uglify-es').default; +const using = require('gulp-using'); +const util = require('gulp-util'); + +const PATHS = { + SRC: { + APP: 'GenymotionManager.js', + BASE: './src', + WORKER: './src/worker', + TEMPLATES: './src/templates', + ASSETS: { + STYLES: './src/scss' + } + }, + DEST: { + BASE: 'dist', + TEMPLATES: 'dist/templates', + ASSETS: { + CSS: 'dist/css' + }, + LIB: { + JS: 'dist/js', + CSS: 'dist/css' + } + }, + TEST: { + UTIL: './test/util', + UT: './test/specs', + E2E: './test/e2e' + } + +}; + +function getTemplateStylesStream() { + return gulp.src([PATHS.SRC.TEMPLATES + '/**/*.css']) + .pipe(base64()); +} + +function getTemplatesStream() { + return merge2( + gulp.src([PATHS.SRC.TEMPLATES + '/**/*.html']), + gulp.src([PATHS.SRC.TEMPLATES + '/**/*.js']), + getTemplateStylesStream() + ).pipe(templateCollector()); +} + +// Clean dist dir +gulp.task('clean', function(cb) { + del([PATHS.DEST.BASE]).then(function() { + cb(); + }, function(err) { + cb(err); + }); +}); + +// HTML templates +gulp.task('app-partials', function() { + return gulp.src(['*.html']) + .pipe(gulpif(util.env.debug, using())) + .pipe(gulpif(util.env.production, minifyHtml({empty: true}))) + .pipe(gulp.dest(PATHS.DEST.BASE)); +}); + +// SASS styles +gulp.task('app-styles', function() { + return gulp.src(PATHS.SRC.ASSETS.STYLES + '/**/*.scss') + .pipe(gulpif(util.env.debug, using())) + .pipe(sass().on('error', sass.logError)) + .pipe(base64()) + .pipe(autoprefixer()) + .pipe(gulpif(util.env.production, minifyCss())) + .pipe(concat('gm-player.css')) + .pipe(gulp.dest(PATHS.DEST.ASSETS.CSS)); +}); + +gulp.task('app-templates', function() { + return getTemplatesStream() + .pipe(tap(function(file) { + // After getting the templates to a JSON form, we need to transform it to a javascript string + templates = file.contents.toString(); + templates = templates.replace(/\\n/g, ''); // we remove \n + templates = templates.replace(/\\"/g, 'xFic1RoIH8'); // we change \" to a tmp replacement string + templates = templates.replace(/'/g, '\\\''); // we change all ' to \' + templates = templates.replace(/"/g, '\''); // we replace all " by ' + templates = templates.replace(/xFic1RoIH8/g, '\\"'); // we change back the tmp replacement string to \" + })); +}); + +function getBundler() { + return browserify({ + entries: [PATHS.SRC.BASE + '/' + PATHS.SRC.APP], + standalone: 'GenymotionManager', + debug: true + }).transform(graspify, ['#GEN_TEMPLATES', templates]); +} + +gulp.task('app-js', function() { + return merge2(getBundler().bundle() + .pipe(source(PATHS.SRC.APP)), {end: true}) + .pipe(gulpif(util.env.debug, using())) + .pipe(gulpif(util.env.production, streamify(babel({ + compact: false, + presets: [ + [ + '@babel/env', { + targets: 'last 2 versions, not dead, not ie 11, not ie_mob 11, not op_mini all, not and_uc 12' + } + ] + ] + })))) + .pipe(streamify(concat('gm-player.min.js'))) + .pipe(gulpif(util.env.production, streamify(uglify()))) + .pipe(gulp.dest(PATHS.DEST.LIB.JS)); +}); + +// Dependencies injection +gulp.task('inject', function() { + return gulp.src('*.html') + .pipe(inject( + gulp.src([ + PATHS.DEST.LIB.CSS + '/**/*.css', + PATHS.DEST.LIB.JS + '/**/*.js' + ], {read: false}) + , { + addRootSlash: false, + ignorePath: PATHS.DEST.BASE + } + )) + .pipe(gulp.dest(PATHS.DEST.BASE)); +}); + +// Simple webserver +gulp.task('connect', function() { + browserSync.init({ + server: { + baseDir: PATHS.DEST.BASE + }, + port: 8000 + }); +}); + +// Build project +gulp.task('build', gulp.series( + 'clean', + 'app-templates', + gulp.parallel('app-partials', 'app-styles', 'app-js'), + 'inject', + function(cb) { + cb(); + } +)); + +// Watch project update +gulp.task('watch', gulp.series('build', function() { + gulp.watch([ + PATHS.SRC.ASSETS.STYLES + '/**/*.scss' + ], gulp.series('app-styles')); + + gulp.watch([ + PATHS.TEST.UT + '/**/*.js' + ], gulp.series('test')); + + gulp.watch([ + PATHS.SRC.BASE + '/*.js', + PATHS.SRC.BASE + '/**/*.js', + PATHS.SRC.WORKER + '/*.js', + PATHS.SRC.WORKER + '/**/*.js', + PATHS.SRC.TEMPLATES + '/**/*' + ], gulp.series('app-js')); +})); + +// Serve project +gulp.task('serve', gulp.series('watch', function(cb) { + browserSync.init({ + files: [PATHS.DEST.BASE + '/**/*'], + server: { + baseDir: PATHS.DEST.BASE + }, + port: 8000 + }); + + cb(); +})); diff --git a/package.json b/package.json new file mode 100644 index 00000000..8996efe2 --- /dev/null +++ b/package.json @@ -0,0 +1,73 @@ +{ + "name": "@genymotion/device-web-player", + "version": "1.4.1", + "description": "Genymotion Virtual Device Web Player", + "main": "dist/js/gm-player.min.js", + "engines": { + "node": ">=12" + }, + "scripts": { + "test": "jest --config tests/config.js", + "lint": "eslint src tests gulp", + "checkstyle": "eslint -f checkstyle -o ./tests/reports/lint/report.xml src tests gulp", + "validate": "yarn lint; yarn checkstyle", + "build": "gulp build --production" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Genymobile/genymotion-device-web-player.git" + }, + "author": "Genymotion team (https://www.genymotion.com)", + "license": "MIT", + "bugs": { + "url": "https://github.com/Genymobile/genymotion-device-web-player/issues" + }, + "homepage": "https://github.com/Genymobile/genymotion-device-web-player#readme", + "dependencies": { + "lodash": "^4.17.20", + "loglevel": "^1.7.0", + "webrtc-adapter": "^7.7.0" + }, + "devDependencies": { + "@babel/core": "^7.12.3", + "@babel/preset-env": "^7.12.1", + "@babel/register": "^7.12.1", + "babel-eslint": "^10.1.0", + "browser-sync": "^2.26.13", + "browserify": "^17.0.0", + "browserify-transform-tools": "^1.7.0", + "del": "^6.0.0", + "eslint": "^7.12.1", + "eslint-plugin-jest": "^24.1.0", + "estraverse-fb": "^1.3.2", + "grasp": "^0.6.0", + "gulp": "^4.0.2", + "gulp-autoprefixer": "^7.0.1", + "gulp-babel": "^8.0.0", + "gulp-clean-css": "^4.3.0", + "gulp-concat": "^2.6.1", + "gulp-connect": "^5.0.0", + "gulp-css-base64": "^2.0.0", + "gulp-html-to-js": "0.0.7", + "gulp-htmlmin": "^5.0.1", + "gulp-if": "^3.0.0", + "gulp-inject": "^5.0.5", + "gulp-sass": "^4.1.0", + "gulp-streamify": "^1.0.2", + "gulp-tap": "^2.0.0", + "gulp-uglify-es": "^2.0.0", + "gulp-using": "^0.1.1", + "gulp-util": "^3.0.8", + "jest": "^26.6.3", + "jest-junit": "^12.0.0", + "merge2": "^1.4.1", + "through2": "^4.0.2", + "vinyl": "^2.2.1", + "vinyl-source-stream": "^2.0.0" + }, + "jest-junit": { + "suiteName": "jest tests", + "outputDirectory": "tests/reports", + "outputName": "junit.xml" + } +} diff --git a/src/GenymotionInstance.js b/src/GenymotionInstance.js new file mode 100644 index 00000000..4d29a0f2 --- /dev/null +++ b/src/GenymotionInstance.js @@ -0,0 +1,830 @@ +'use strict'; + +// Plugins +const PeerConnectionStats = require('./plugins/PeerConnectionStats'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +/** + * Genymotion player instance. + * Initialize a player for a specific instance + */ +module.exports = class GenymotionInstance { + /** + * Player instance initialization. + * + * @param {HTMLElement} domRoot DOM element to attach the player to. + * @param {Object} options Instance configuration options. + */ + constructor(domRoot, options) { + this.timeoutCallbacks = []; + this.videoBackupStyleBackground = ''; + + // Options associated with this instance + this.options = options; + + // Websocket & WebRTC connection state + this.initialized = false; + this.reconnecting = false; + + // Enabled features + this.keyboardEventsEnabled = false; + this.touchEventsEnabled = false; + this.mouseEventsEnabled = false; + + // Websocket + this.webRTCWebsocket = null; + this.useWebsocketAsDataChannel = false; + + // DOM elements + this.root = domRoot; + this.video = this.getChildByClass(this.root, 'gm-video'); + this.wrapper = this.getChildByClass(this.root, 'gm-wrapper'); + this.videoWrapper = this.getChildByClass(this.root, 'gm-video-wrapper'); + this.stream = null; + + // Event callbacks + this.callbacks = {}; + + // WebRTC related attributes + this.peerConnection = null; + this.signalingDataChannel = null; + this.cameraSender = null; + this.microphoneSender = null; + this.sdpConstraints = { + offerToReceiveAudio: true, + offerToReceiveVideo: true, + }; + + // last accessed x/y position + this.x = 0; + this.y = 0; + + document.addEventListener('click', (event) => { + if (!this.hasSomeParentTheClass(event.target, 'gm-overlay') + && !event.target.classList.contains('gm-icon-button') + && !event.target.classList.contains('gm-dont-close')) { + this.emit('close-overlays'); + } + }); + } + + /** + * Create & dispatch a custom event. + * + * @param {string} name Event name. + * @param {Object} payload Event payload. + */ + dispatchEvent(name, payload) { + window.dispatchEvent(new CustomEvent(name, {detail: payload})); + } + + /** + * Find a child of a DOM element of given a class. + * + * @param {HTMLElement} element DOM element to search into. + * @param {string} className Name of the class to search for. + * @return {HTMLElement} The first DOM element found, or null. + */ + getChildByClass(element, className) { + for (let i = 0; i < element.childNodes.length; i++) { + const node = element.childNodes[i]; + if (node.classList && node.classList.contains(className)) { + return node; + } + + const child = this.getChildByClass(node, className); + if (child) { + return child; + } + } + return null; + } + + /** + * Register new callback to be executed when an event is emitted. + * + * @param {string} eventTag Tag of event to react to. + * @param {Function} callback Action to execute to handle the event. + */ + registerEventCallback(eventTag, callback) { + if (!this.callbacks[eventTag]) { + this.callbacks[eventTag] = []; + } + this.callbacks[eventTag].push(callback); + } + + /** + * Emit a new event. + * + * @param {string} eventTag Tag of the event. + * @param {Object} payload Event payload. + */ + emit(eventTag, payload) { + if (!this.callbacks[eventTag]) { + return; + } + + this.callbacks[eventTag].forEach((callback) => { + callback(payload); + }); + } + + /** + * Look for a class applied in a element parent tree. + * + * @param {HTMLElement} element DOM element to check. + * @param {string} className Class name to look for. + * @return {boolean} Whether or not the class has been found in the given element parents. + */ + hasSomeParentTheClass(element, className) { + if (element.classList && element.classList.contains(className)) { + return true; + } + return element.parentNode && this.hasSomeParentTheClass(element.parentNode, className); + } + + /** + * Check a websocket instance status. + * + * @param {WebSocket} webSocket websocket instance. + * @return {boolean} Whether or not the websocket is open. + */ + isWebsocketOpen(webSocket) { + return webSocket && webSocket instanceof WebSocket && webSocket.readyState === webSocket.OPEN; + } + + /** + * WebRTC channel ready callback. + */ + onWebRTCReady() { + this.webRTCConnectionRetryCount = 0; + this.openWebRTCConnection(); + } + + /** + * Open WebSocket and WebRTC connection. + */ + openWebRTCConnection() { + if (this.webRTCWebsocket) { + this.disconnect(); + this.reconnecting = true; + } + + this.webRTCWebsocket = new WebSocket(this.options.webRTCUrl); + this.webRTCWebsocket.onopen = this.sendAuthenticationToken.bind(this); + this.webRTCWebsocket.onmessage = this.onWebSocketMessage.bind(this); + this.webRTCWebsocket.onerror = this.onWebSocketMessage.bind(this); + this.webRTCWebsocket.binaryType = 'arraybuffer'; + + /** + * In case of unexpected close of the WebSocket connection, the handler will retry n times before dying + * For now we'll be using a lucky value until behaviour is further specified + */ + if (this.webRTCConnectionRetryCount <= 13) { + this.onConnectionClosed(); + } else { + log.error('Unable to establish connection to your instance.'); + } + } + + /** + * WebRTC / WebSocket connection closed callback. + * This will poll the WebRTC sequence until the WebSocket stops throwing the onclose event (unexpected quit). + */ + onConnectionClosed() { + this.webRTCWebsocket.onclose = (event) => { + this.video.style.background = this.videoBackupStyleBackground; + this.initialized = false; + log.debug('Error! Maybe your VM is not available yet? (' + event.code +') ' + event.reason); + + switch (event.code) { + case 1000: + case 1001: + case 1005: + log.debug('Closing websocket'); + this.dispatchEvent('closeConnection', {msg: 'Closing connection'}); + break; + + case 1002: + case 1003: + case 1006: + case 1007: + case 1008: + case 1009: + case 1010: + case 1011: + case 1012: + case 1013: + case 1014: + case 1015: { + // Might be interesting to be able to setup polling debounce in the object configuration (DOM / Frontend Portal) + this.dispatchEvent('closeConnectionUnavailable', {msg: 'Can\'t connect to the WebSocket'}); + log.debug('Retrying in 3 seconds...'); + + const timeout = setTimeout(() => { + this.openWebRTCConnection(); + }, 3000); + this.timeoutCallbacks.push(timeout); + this.webRTCConnectionRetryCount++; + break; + } + case 4242: // wrong token provided + this.dispatchEvent('closeWrongToken', {msg: 'Wrong token, can\'t establish connection'}); + break; + + case 4243: // token no longer valid + this.dispatchEvent('closeNoLongerValidToken', { + msg: 'The token provided is no longer valid', + }); + break; + + case 4244: // server is shutting down + this.dispatchEvent('closeServerShutdown', {msg: 'Server is shutting down...'}); + break; + + default: + this.dispatchEvent('defaultCloseConnection', {msg: 'Default close connection'}); + // Do nothing (for now) + break; + } + }; + } + + /** + * Send auhentication token through the WebSocket connection. + */ + sendAuthenticationToken() { + const tokenRequest = { + type: 'token', + token: this.options.token, + }; + + if (this.isWebsocketOpen(this.webRTCWebsocket)) { + this.webRTCWebsocket.send(JSON.stringify(tokenRequest)); + } + } + + /** + * Disconnect the current instance, closing any open data channels. + */ + disconnect() { + this.initialized = false; + + if (typeof this.camera !== 'undefined' && typeof this.camera.getClientVideoStream !== 'undefined') { + this.removeLocalStream(this.camera.getClientVideoStream()); + } + + if (this.webRTCWebsocket) { + this.webRTCWebsocket.close(); + this.webRTCWebsocket = null; + this.timeoutCallbacks.forEach((timeout) => { + clearTimeout(timeout); + }); + this.timeoutCallbacks = []; + } + + if (this.peerConnection) { + this.peerConnection.close(); + this.peerConnection = null; + } + + if (this.signalingDataChannel) { + this.signalingDataChannel.close(); + this.signalingDataChannel = null; + } + + if (typeof this.fileUpload !== 'undefined') { + const msg = {type: 'close'}; + this.fileUpload.loaderWorker.postMessage(msg); + } + } + + /** + * Send event to the instance through the Websocket connection. + * + * @param {Object} event Event to send. + */ + sendEvent(event) { + event = JSON.stringify(event); + + if (this.useWebsocketAsDataChannel) { + if (this.isWebsocketOpen(this.webRTCWebsocket)) { + this.webRTCWebsocket.send(event); + } + } else { + if (this.signalingDataChannel && this.signalingDataChannel.readyState === 'open') { + this.signalingDataChannel.send(event); + } else { + log.warn('cannot send event, signaling data channel closed'); + } + } + } + + /** + * Reconfigure & setup the peer-to-peer connection (SDP). + * Can be used anytime to renegotiate the SDP if necessary. + */ + renegotiateWebRTCConnection() { + // For safari we add audio & video before sending SDP to avoid empty SDP + const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + if (isSafari) { + this.peerConnection.addTransceiver('audio'); + this.peerConnection.addTransceiver('video'); + } + // creating SDP offer + this.peerConnection.createOffer( + this.setLocalDescription.bind(this), + this.onWebRTCConnectionError.bind(this), + this.sdpConstraints + ); + } + + /** + * Find RTCRtpSender corresponding RTCRtpTransceiver and set its direction. + * + * @param {RTCRtpSender} sender Peer (sender). + * @param {string} direction Transceiver direction. + */ + setTransceiverDirection(sender) { + // find transceiver that contains sender + this.peerConnection.getTransceivers().forEach((transceiver) => { + if (transceiver.sender === sender) { + transceiver.direction = 'sendrecv'; + } + }); + } + + /** + * Add a local stream and send it through SDP renegotiation. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack + * and https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream + * + * @param {MediaStream} stream Stream to add. + */ + addLocalStream(stream) { + if (typeof this.peerConnection.addTrack === 'function') { + // If the new API "addTrack" is available, we use it + + /** + * If cameraSender or microphoneSender is defined, this means that we already added + * it to the PeerConnection. Since removeTrack() just remove the track + * from it, re-add it and switch back the RTCRtpTranceiver direction + * to send and recv. + */ + if (stream.getVideoTracks().length > 0) { + if (this.cameraSender) { + log.debug('Replacing video track on sender'); + this.cameraSender.replaceTrack(stream.getVideoTracks()[0]); + this.setTransceiverDirection(this.cameraSender, 'sendrecv'); + } else { + this.cameraSender = this.peerConnection.addTrack(stream.getVideoTracks()[0], + stream); + } + } + + if (this.options.microphone && stream.getAudioTracks().length > 0) { + if (this.microphoneSender) { + log.debug('Replacing audio track on sender'); + this.microphoneSender.replaceTrack(stream.getAudioTracks()[0]); + this.setTransceiverDirection(this.microphoneSender, 'sendrecv'); + } else { + this.microphoneSender = this.peerConnection.addTrack(stream.getAudioTracks()[0], + stream); + } + } + } else { + // Else if it is not available, we use the old "addStream" + this.peerConnection.addStream(stream); + } + this.renegotiateWebRTCConnection(); + } + + /** + * Remove a local stream and stop sending it through SDP renegotiation. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeTrack + * and https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeStream + * + * @param {MediaStream} stream Stream to stop and remove. + */ + removeLocalStream(stream) { + if (stream instanceof MediaStream) { + stream.getTracks().forEach((track) => { + track.stop(); + }); + // If the new API "removeTrack" is available, we use it + if (typeof this.peerConnection.addTrack === 'function') { + this.peerConnection.removeTrack(this.cameraSender); + if (this.microphoneSender) { + this.peerConnection.removeTrack(this.microphoneSender); + } + // Else if it is not available, we use the old "removeStream" + } else { + this.peerConnection.removeStream(stream); + } + this.renegotiateWebRTCConnection(); + } + } + + /** + * Creates a peer connection that connects through WebRTC to the instance. + */ + createPeerConnection() { + log.debug('Creating peer connection'); + + const iceServers = []; + + if (Object.keys(this.options.stun).length > 0) { + iceServers.push(this.options.stun); + } + + if (Object.keys(this.options.turn).length > 0) { + iceServers.push(this.options.turn); + } + + const config = { + iceServers: iceServers, + }; + + if (!this.peerConnection) { + try { + this.peerConnection = new RTCPeerConnection(config); + } catch (e) { + log.warn('Failed to create PeerConnection, exception:', e.message); + } + } else { + log.debug('Peer connection already exists'); + } + + if (typeof this.peerConnection.createDataChannel !== 'undefined') { + const dataChannelOptions = { + ordered: true, + }; + + this.signalingDataChannel = this.peerConnection.createDataChannel('events', dataChannelOptions); + + this.signalingDataChannel.onerror = (error) => { + log.warn('Data Channel Error:', error); + }; + + this.signalingDataChannel.onmessage = (event) => { + log.debug('Got Data Channel Message:', event.data); + }; + this.signalingDataChannel.onopen = () => { + log.debug('Data Channel opened'); + }; + + this.signalingDataChannel.onclose = () => { + log.debug('The Data Channel is Closed'); + }; + + this.peerConnection.ondatachannel = (event) => { + const answererDataChannel = event.channel; + answererDataChannel.onmessage = this.onDataChannelMessage.bind(this); + }; + } else { + this.useWebsocketAsDataChannel = true; + } + + this.peerConnection.onicecandidate = (event) => { + if (typeof event.candidate === 'undefined') { + // Nothing we can do + return; + } + + if (event.candidate) { + this.sendIceCandidate(event.candidate); + } else { + log.debug('All candidates ready'); + } + }; + + this.peerConnection.ontrack = (event) => { + log.debug('Received track:', event.track.kind); + + if (event.track.kind !== 'video') { + return; + } + + log.debug('Added remote video track using ontrack'); + this.video.srcObject = event.streams[0]; + this.video.setAttribute('playsinline', true); // needed on iOs + this.stream = event.streams[0]; + + // Get the PeerConnection Statistics after 3 seconds + new PeerConnectionStats(this, this.peerConnection, 3000); + + this.videoBackupStyleBackground = this.video.style.background; + this.video.style.background = 'transparent'; + + if (this.touchEventsEnabled) { + this.touchEvents.addTouchCallbacks(); + } + + if (this.mouseEventsEnabled) { + this.mouseEvents.addMouseCallbacks(); + } + + if (this.keyboardEventsEnabled) { + this.keyboardEvents.addKeyboardCallbacks(); + } + + const playWithSound = this.video.play(); // needed on Safari (web & iOs) + if (!playWithSound) { + return; + } + + playWithSound.then(() => { + log.debug('Playing video with sound enabled'); + this.dispatchEvent('video', {msg: 'play automatically allowed with sound'}); + }).catch(() => { + log.debug('Can\'t play video with sound enabled'); + this.dispatchEvent('video', {msg: 'play with sound denied'}); + this.video.muted = true; + const playWithoutSound = this.video.play(); + playWithoutSound.then(() => { + log.debug('Playing video with sound disabled'); + this.dispatchEvent('video', {msg: 'play automatically allowed without sound'}); + const popup = document.createElement('div'); + popup.classList.add('gm-click-to-unmute'); + popup.innerHTML = 'By default, the sound has been turned off, ' + + 'please click anywhere to re-enable audio'; + this.videoWrapper.prepend(popup); + const addSound = () => { + this.video.muted = false; + this.video.removeEventListener('click', addSound); + this.video.removeEventListener('touchend', addSound); + this.dispatchEvent('video', {msg: 'sound manually allowed by click'}); + popup.remove(); + log.debug('Playing video with sound enabled has been authorized due to user click'); + }; + this.video.addEventListener('click', addSound); + this.video.addEventListener('touchend', addSound); + }).catch(() => { + log.debug('Can\'t play video, even with sound disabled'); + this.dispatchEvent('video', {msg: 'play denied even without sound'}); + const div = document.createElement('div'); + this.classList.add('gm-click-to-display'); + this.classList.add('gm-video-overlay'); + this.videoWrapper.prepend(div); + const allowPlay = () => { + this.video.play(); + div.remove(); + this.dispatchEvent('video', {msg: 'play manually allowed by click'}); + log.debug('Playing video with sound disabled has been authorized due to user click'); + }; + div.addEventListener('click', allowPlay); + div.addEventListener('touchend', allowPlay); + }); + }); + }; + + this.peerConnection.oniceconnectionstatechange = () => { + const iceConnectionState = + this.peerConnection ? this.peerConnection.iceConnectionState : 'No peerConnection'; + log.debug('iceConnectionState: ', iceConnectionState); + this.dispatchEvent('iceConnectionState', {msg: iceConnectionState}); + + if (iceConnectionState !== 'failed') { + return; + } + + let message = '

'; + const div = document.createElement('div'); + div.classList.add('gm-overlay-cant-connect'); + div.classList.add('gm-video-overlay'); + this.videoWrapper.prepend(div); + if (this.options.connectionFailedURL) { + message = message.replace( + '{DOC_AVAILABLE}', + '
See
help to setup TURN configuration.' + ); + const openDocumentationLink = () => { + this.dispatchEvent('iceConnectionStateDocumentation', {msg: 'clicked'}); + div.remove(); + window.open(this.options.connectionFailedURL, '_blank'); + }; + div.addEventListener('click', openDocumentationLink); + div.addEventListener('touchend', openDocumentationLink); + } else { + message = message.replace('{DOC_AVAILABLE}', ''); + } + + div.innerHTML = message; + }; + + this.peerConnection.onconnectionstatechange = () => { + log.debug('ConnectionState changed:', this.peerConnection.iceConnectionState); + if (this.peerConnection.iceConnectionState === 'disconnected') { + this.onWebRTCReady(); + } + }; + + this.peerConnection.onnegotiationneeded = () => { + log.debug('on Negotiation needed'); + }; + + this.renegotiateWebRTCConnection(); + } + + /** + * Send any ice candidates to the other peer. + * + * @param {RTCIceCandidate} iceCandidate Candidate to send. + */ + sendIceCandidate(iceCandidate) { + if (this.isWebsocketOpen(this.webRTCWebsocket)) { + this.webRTCWebsocket.send(JSON.stringify(iceCandidate)); + } + } + + /** + * Called when a PeerConnection receives a description. + * + * @param {RTCSessionDescription} description Peer connection session description. + */ + setLocalDescription(description) { + this.peerConnection.setLocalDescription(description); + if (this.isWebsocketOpen(this.webRTCWebsocket)) { + this.webRTCWebsocket.send(JSON.stringify(description)); + } + } + + /** + * Called when the WebRTC WebSocket socket receives a message. + * + * @param {Event} message WebRTC message. + */ + onWebSocketMessage(message) { + let data = null; + + try { + data = JSON.parse(message.data); + } catch (error) { + return; + } + + if (!data) { + return; + } + + if (data.sdp) { + try { + const sdp = new RTCSessionDescription(data); + this.peerConnection.setRemoteDescription( + sdp, + this.onWebRTCConnectionEstablished.bind(this), + this.onWebRTCConnectionError.bind(this) + ); + } catch (error) { + log.warn('Failed to create SDP ', error.message); + } + } else if (data.candidate) { + try { + const candidate = new RTCIceCandidate(data); + this.peerConnection.addIceCandidate(candidate); + } catch (error) { + log.warn('Failed to create ICE ', error.message); + } + } else if (data.connection) { + this.createPeerConnection(); + } else { + this.handleMessage(data); + } + } + + /** + * Called when both local and remote SDP have been set. + */ + onWebRTCConnectionEstablished() { + log.debug('WebRTC connection success'); + + if (this.initialized) { + return; + } + + this.initialized = true; + if (this.reconnecting) { + this.reconnecting = false; + } else { + this.dispatchEvent('successConnection', {msg: 'Connection established'}); + } + + if (this.fileUpload) { + const msg = { + type: 'address', + fileUploadAddress: this.options.fileUploadUrl, + token: this.options.token, + }; + + if (this.fileUpload.loaderWorker) { + this.fileUpload.loaderWorker.postMessage(msg); + } + } + } + + /** + * WebRTC connection error. + * + * @param {string} code Error code. + */ + onWebRTCConnectionError(code) { + log.debug('Failure callback:', code); + } + + /** + * Parse & process data events received from WebRTC data channel. + * + * @param {Event} event WebRTC event. + */ + onDataChannelMessage(event) { + const data = JSON.parse(event.data); + this.handleMessage(data); + } + + /** + * Process WebRTC message. + * + * @param {Object} data Message payload. + */ + handleMessage(data) { + if (data.type === 'USERS') { + if (data.code === 'SUCCESS') { + this.dispatchEvent('userListUpdated', {msg: data.message}); + } + } else if (data.type === 'VERSION') { + if (data.code === 'SUCCESS') { + const versionNumber = this.getChildByClass(this.root, 'gm-version-number'); + if (versionNumber) { + versionNumber.innerHTML = data.message; + } + } + } else if (data.type === 'CAPABILITIES') { + [{ + widget: this.battery, + capability: data.message.battery, + }, { + widget: this.camera, + capability: data.message.camera, + }, { + widget: this.gps, + capability: data.message.gps, + }, { + widget: this.identifiers, + capability: data.message.identifiers, + }, { + widget: this.network, + capability: data.message.network, + }, { + widget: this.phone, + capability: data.message.phone, + }, { + widget: this.diskIO, + capability: data.message.diskIO, + }, { + widget: this.buttonsEvents, + capability: data.message.accelerometer, + enable: (widget) => { + widget.enableRotation(); + }, + disable: (widget) => { + widget.disableRotation(); + }, + }, { + widget: this.fileUpload, + capability: data.message.systemPatcher, + enable: (widget) => { + widget.setAvailability(true); + }, + disable: (widget) => { + widget.setAvailability(false); + }, + }].forEach((feature) => { + if (typeof feature.widget !== 'undefined') { + if (feature.capability === false) { + if (feature.disable) { + feature.disable(feature.widget); + } else { + feature.widget.disable(); + } + } else { + if (feature.enable) { + feature.enable(feature.widget); + } else { + feature.widget.enable(); + } + } + } + }); + } + + // Emit message to anyone who wants to listen + if (data.code === 'SUCCESS') { + this.emit(data.type, data.message); + } + } +}; diff --git a/src/GenymotionManager.js b/src/GenymotionManager.js new file mode 100644 index 00000000..8b7236fa --- /dev/null +++ b/src/GenymotionManager.js @@ -0,0 +1,234 @@ +'use strict'; + +const GenymotionInstance = require('./GenymotionInstance'); +const defaultsDeep = require('lodash/defaultsDeep'); + +// Plugins +const GPS = require('./plugins/GPS'); +const CoordinateUtils = require('./plugins/CoordinateUtils'); +const MouseEvents = require('./plugins/MouseEvents'); +const MultiTouchEvents = require('./plugins/MultiTouchEvents'); +const ButtonsEvents = require('./plugins/ButtonsEvents'); +const Fullscreen = require('./plugins/Fullscreen'); +const KeyboardEvents = require('./plugins/KeyboardEvents'); +const Clipboard = require('./plugins/Clipboard'); +const FileUpload = require('./plugins/FileUpload'); +const Camera = require('./plugins/Camera'); +const Battery = require('./plugins/Battery'); +const StreamBitrate = require('./plugins/StreamBitrate'); +const Screencast = require('./plugins/Screencast'); +const Identifiers = require('./plugins/Identifiers'); +const Network = require('./plugins/Network'); +const Phone = require('./plugins/Phone'); +const StreamResolution = require('./plugins/StreamResolution'); +const IOThrottling = require('./plugins/IOThrottling'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +// Templates are loaded dynamically from the `templates` folder +const TEMPLATE_JS = 'genymotion-js'; +const TEMPLATE_CSS = 'genymotion-css'; + +// Default options +const defaultOptions = { + template: 'player', + touch: true, + mouse: true, + volume: true, + rotation: true, + navbar: true, + power: true, + keyboard: true, + fullscreen: true, + camera: true, + microphone: false, + fileUpload: true, + streamBitrate: false, + clipboard: true, + battery: true, + gps: true, + gpsSpeedSupport: false, + capture: true, + identifiers: true, + network: true, + baseband: false, + phone: true, + streamResolution: true, + diskIO: true, + translateHomeKey: false, + token: '', + i18n: {}, + stun: { + urls: [ + // TODO: remove what we don't have + 'stun:stun-eu.genymotion.com:80', + 'stun:stun-eu.genymotion.com:443', + 'stun:stun-eu.genymotion.com:3478', + 'stun:stun-eu.genymotion.com:5349', + 'stun:stun-na.genymotion.com:80', + 'stun:stun-na.genymotion.com:443', + 'stun:stun-na.genymotion.com:3478', + 'stun:stun-na.genymotion.com:5349' + ], + }, + connectionFailedURL: '', + turn: {}, +}; + +/** + * Setup & create instances of the Genymotion player + */ +module.exports = class GenymotionManager { + constructor() { + this.instances = []; + /* global GEN_TEMPLATES */ + this.templates = GEN_TEMPLATES; + } + + /** + * Setup a device player instance in the given dom element, for the Genymotion Cloud instance identified by its instanceWebRTCUrl. + * + * @param {HTMLElement|string} dom The DOM element (or its ID) to setup the device player into. + * @param {string} webRTCUrl WebRTC URL of the instance. + * @param {Object} options Various configuration options. + * @param {string} options.template Template to use. Default: 'player'. + * @param {boolean} options.touch Touch support activated. Default: true. + * @param {boolean} options.mouse Mouse support activated. Default: true. + * @param {boolean} options.volume Audio volume control support activated. Default: true. + * @param {boolean} options.rotation Screen rotation support activated. Default: true. + * @param {boolean} options.navbar Android navbar support activated. Default: true. + * @param {boolean} options.power Power control support activated. Default: true. + * @param {boolean} options.keyboard Keyboad support activated. Default: true. + * @param {boolean} options.fullscreen Fullscreen support activated. Default: true. + * @param {boolean} options.camera Camera support activated. Default: true. + * @param {boolean} options.microphone Microphone support activated. Default: false. + * @param {boolean} options.fileUpload File upload support activated. Default: true. + * @param {string} options.fileUploadUrl File upload URL. Required if fileUpload===true. + * @param {boolean} options.streamBitrate Stream bitrate control support activated. Default: false. + * @param {boolean} options.clipboard Clipboard forwarding support activated. Default: true. + * @param {boolean} options.battery Battery support activated. Default: true. + * @param {boolean} options.gps GPS support activated. Default: true. + * @param {boolean} options.gpsSpeedSupport GPS speed support activated. Default: false. + * @param {boolean} options.capture Screen capture support activated. Default: true. + * @param {boolean} options.identifiers Identifiers (IMEI, etc...) support activated. Default: true. + * @param {boolean} options.network Network throttling support activated. Default: true. + * @param {boolean} options.baseband Baseband controll support activated. Default: false. + * @param {boolean} options.phone Baseband support activated. Default: true. + * @param {boolean} options.streamResolution Stream resolution control support activated. Default: true. + * @param {boolean} options.diskIO Disk I/O throttling support activated. Default: true. + * @param {boolean} options.translateHomeKey Whether or not the HOME key button should be decompose to META + ENTER. Default: false. + * @param {string} options.token Instance access token (JWT). Default: ''. + * @param {Object} options.i18n Translations keys for the UI. Default: {}. + * @param {Object} options.stun WebRTC STUN servers configuration. + * @param {Array} options.stun.urls WebRTC STUN servers URLs. + * @param {string} options.connectionFailedURL Redirection page in case of connection establishment error. + * @param {Object} options.turn WebRTC TURN servers configuration. + * @param {Array} options.turn.urls WebRTC TURN servers URLs. + * @param {string} options.turn.username WebRTC TURN servers username. + * @param {string} options.turn.credential WebRTC TURN servers password. + * @param {boolean} options.turn.default Whether or not we should use the TURN servers by default. Default: false. + * @return {GenymotionInstance} The Genymotion instance. + */ + setupInstance(dom, webRTCUrl, options) { + if (typeof dom === 'string') { + dom = document.getElementById(dom); + } + + if (typeof options === 'undefined') { + options = {}; + } + options = defaultsDeep(options, defaultOptions); + options.webRTCUrl = webRTCUrl; + // if we have at least one button to setup, we will instantiate the "buttons" plugin + options.buttons = options.volume || options.rotation || options.navbar || options.power; + + log.debug('Creating genymotion display on ' + webRTCUrl); + dom.classList.add('gm-player-instance'); + dom.classList.add('gm-template-' + options.template); + document.body.classList.add('gm-template-' + options.template + '-body'); + + return this.loadTemplate(dom, this.templates[options.template], options); + } + + /** + * Loads the selected template. + * + * @param {HTMLElement} dom The DOM element to setup the device player into. + * @param {string} template Template to use. + * @param {Object} options Various configuration options. + * @return {GenymotionInstance} The Genymotion instance. + */ + loadTemplate(dom, template, options) { + const head = document.getElementsByTagName('head')[0]; + const scriptId = TEMPLATE_JS + '-' + options.template; + const styleId = TEMPLATE_CSS + '-' + options.template; + + // Handle template JS + if (!document.getElementById(scriptId)) { + const script = document.createElement('script'); + script.id = scriptId; + script.text = template.js; + head.appendChild(script); + } + + // Handle template CSS + if (!document.getElementById(styleId)) { + const style = document.createElement('style'); + style.id = styleId; + style.appendChild(document.createTextNode(template.css)); + head.appendChild(style); + } + + // Handle template dom + dom.innerHTML = template.html; + + // Kick off next phase of setup + return this.addPlugins(dom, options); + } + + /** + * Initialize all the needed plugins. + * + * @param {HTMLElement} dom The DOM element to setup the device player into. + * @param {Object} options Various configuration options. + * @return {GenymotionInstance} The Genymotion instance. + */ + addPlugins(dom, options) { + const instance = new GenymotionInstance(dom, options); + this.instances.push(instance); + + const pluginInitMap = [ + {enabled: options.touch || options.mouse, class: CoordinateUtils}, + {enabled: options.mouse, class: MouseEvents}, + {enabled: options.touch, class: MultiTouchEvents}, + {enabled: options.fullscreen, class: Fullscreen}, + {enabled: options.keyboard, class: KeyboardEvents}, + {enabled: options.clipboard, class: Clipboard, params: [options.i18n]}, + {enabled: options.fileUpload, class: FileUpload, params: [options.i18n]}, + {enabled: options.camera, class: Camera, params: [options.i18n]}, + {enabled: options.battery, class: Battery, params: [options.i18n]}, + {enabled: options.streamBitrate, class: StreamBitrate, params: [options.i18n]}, + {enabled: options.gps, class: GPS, params: [options.i18n, options.gpsSpeedSupport]}, + {enabled: options.capture, class: Screencast, params: [options.i18n]}, + {enabled: options.identifiers, class: Identifiers, params: [options.i18n]}, + {enabled: options.network, class: Network, params: [options.i18n, options.baseband]}, + {enabled: options.phone, class: Phone, params: [options.i18n]}, + {enabled: options.streamResolution, class: StreamResolution}, + {enabled: options.diskIO, class: IOThrottling, params: [options.i18n]}, + {enabled: options.buttons, class: ButtonsEvents, params: [options.i18n, options.translateHomeKey]}, + ]; + + pluginInitMap.forEach((plugin) => { + const args = plugin.params || []; + + if (plugin.enabled) { + new plugin.class(instance, ...args); + } + }); + + instance.onWebRTCReady(); + + return instance; + } +}; diff --git a/src/assets/images/hq-over.png b/src/assets/images/hq-over.png new file mode 100644 index 0000000000000000000000000000000000000000..8eab8d6e2e62adbf85f128c8579e0f6187eeecf1 GIT binary patch literal 1300 zcmV+v1?&2WP)gdoHGLm1OkCTAP@)y0)arV=^(5B!OM6~cJJ7804mo>z>S=By!p8*oa#_PbRAu#} zjzm{a8^F1&k*&DvBc6swi^zVGfP-t?O`naVZ{G4Yzg;iQy|yRTcYC!plGU7#xP$&8 zl821WGcce4Eh#9KcjuL@GpVU9DJYc;yv~d>1|h18)qPpBfVBH&Vaz00mT`U%*j z@jRxmdbA?hHL${xe-=KSQBTtm8%Z6D=|aHn0-%2|@W#Hwk*`yE5dl7>;Bx|=Ty~zX zXO!%r2RN@^o9Oy6qoxlr5M2%hdTM7%`7|$5;F_oLSt7in33(+0EoTAQowBXk zx&kl`7z+AN=6hTkG`M=uvH~zrrMo6vHf_^jgP5zVdk7MA`I%O-V`fcem9(LH&~{Ue zkg>1dQ2&{>X{Qp&<(M{WbH*V}m*~bKe#E*dK-i*dha-_$vo_Pwky-scXscxfAZFd4 zt=P7`(5%gpz+Qt5o->sK8!-+)Bm!;`P`o6F(_Rc<#*_l{^Tm)H8j)AFXN^OU>4x^w z0qHT*r4@3(IA~h94DzrpB_vdtPAh{A2L7_F04m|UAtGu_i^_D^FkAd$T>&!lgN6yN zHd+z%tb4L7X>t=$t#-D!BT1(%D?s2z-vq#YnkPCoDQjPtr2hI(`&U8+tIw2Nk9H7j8828JiwO>4>R*|O%%v>$lpcWL4VF;wY$+r z%%`-w&&edj|pPRaH&fdZP`zj7x%{lUY_ zrG=5Wy#t^uxF}bPhmo3Uo1@no1~=lU)wk<5cA5i4Y{FI>V_y zde=rT?wUL`n>PXg_n+&HcpCN>LH}NWK(zpBT>xCp;-xKKDTWz*b{5%CWvpj%wfkA_ zy#N4ICVD1M#5=21l|#CxysI}s)XXW`9n+j4v@wvb}^P0Ss7i6r`-0NXh6pkAOvfFTD9K149cHLn4-XqGb)c zH%$Zpz-U=$yURwu65wB~3bUNumup232m}IwKp+qZ1OkCzlkguoEU@v~-gy}S0000< KMNUMnLSTZyUQi$a literal 0 HcmV?d00001 diff --git a/src/assets/images/hq.png b/src/assets/images/hq.png new file mode 100644 index 0000000000000000000000000000000000000000..731dd5a21c544db199aaf40ae90dcbe37437f465 GIT binary patch literal 937 zcmV;a16KTrP)#5{+MhPtdD^Cl7MrVxkwX zqTVDJJ*^2w8ckZDNWuY^O51I_J3H@RFEv?SDwb|FyLNs_4>Os*nfdR$nKy4VO`%XI z6bgkxp-?Ck3PsQ`3?m|?oB%Kj-~)g)09N(4R;`dl0~v8k2fMME4!X8Q$I9?KDlZ z!*QHpBDxD;u}1r3xm-RSDgm~(wqgK2vx#g;DX;$Nzf#I6?k)g6Wipxe&0z7KeeF4leTTcTm zfMr?z0EGAJmrA8mjS5gIl?DJvk3C^oR)1gxAflV>i!J&+#sR$a+9aYIwb~sCLbRvr z`ei2E+a|;@ORSG8ffYd0v~f0gqX_~S2lWv&0rvT)>$>ZH4N56DSXE!Gc9FyQ^IsHT zU)bt6PR_4E+qQGiE}PAE`4wPq zZ|@j8S@OPIK-YD>fsLqMx5;Xn*8QsxkH@<;P0RZcAQp>tv#NZMUEsU75RFEM{2Giz zB4=4uCddMOWUFnw39$vYu^LzbDwWDI8~kJw1ejobybo+BTr3t(cyDmkyTCxBlP?yF zC%L_RKHn4c+u(DyYd`dH4|u?Ay$Jku*md18HfUD}G15Rj;3CflxUM@EmWU z_F%pJmCNNb05+J7`Ou`qwzs$A09JXbTS_@qN9;dYWAaJUG&@2gfG4l_wMLfrH4)u& z9Ot}gnjJekJ4YSIIY&g({O(_EpZ1ef3H!Uit3sjB>-TI9!pqR~Azy~pi0D@6UxvIS zlgVh+zvrs&fa?H?zY$eV^LZMJ76bgkxp-?Ck{~tdA + + + ic-battery_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-battery_active_black.svg b/src/assets/images/ic-battery_active_black.svg new file mode 100644 index 00000000..117093db --- /dev/null +++ b/src/assets/images/ic-battery_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-battery_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-battery_empty.svg b/src/assets/images/ic-battery_empty.svg new file mode 100644 index 00000000..52991b8b --- /dev/null +++ b/src/assets/images/ic-battery_empty.svg @@ -0,0 +1,34 @@ + + + + ic-battery_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-battery_inactive.svg b/src/assets/images/ic-battery_inactive.svg new file mode 100644 index 00000000..7946ace6 --- /dev/null +++ b/src/assets/images/ic-battery_inactive.svg @@ -0,0 +1,12 @@ + + + + ic-battery_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-close-popup-default.svg b/src/assets/images/ic-close-popup-default.svg new file mode 100644 index 00000000..1f9fa550 --- /dev/null +++ b/src/assets/images/ic-close-popup-default.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/assets/images/ic-close-popup-hover.svg b/src/assets/images/ic-close-popup-hover.svg new file mode 100644 index 00000000..07198ed0 --- /dev/null +++ b/src/assets/images/ic-close-popup-hover.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/assets/images/ic-nav_android_back_active.svg b/src/assets/images/ic-nav_android_back_active.svg new file mode 100644 index 00000000..9671847e --- /dev/null +++ b/src/assets/images/ic-nav_android_back_active.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-nav_android_back_active_black.svg b/src/assets/images/ic-nav_android_back_active_black.svg new file mode 100644 index 00000000..81815590 --- /dev/null +++ b/src/assets/images/ic-nav_android_back_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-nav_android_back_inactive.svg b/src/assets/images/ic-nav_android_back_inactive.svg new file mode 100644 index 00000000..1c1ad24f --- /dev/null +++ b/src/assets/images/ic-nav_android_back_inactive.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_active + Created with Sketch. + + + + + + + diff --git a/src/assets/images/ic-nav_android_home_active.svg b/src/assets/images/ic-nav_android_home_active.svg new file mode 100644 index 00000000..c279d01d --- /dev/null +++ b/src/assets/images/ic-nav_android_home_active.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_home_active_black + Created with Sketch. + + + + + + + diff --git a/src/assets/images/ic-nav_android_home_active_black.svg b/src/assets/images/ic-nav_android_home_active_black.svg new file mode 100644 index 00000000..da1ff673 --- /dev/null +++ b/src/assets/images/ic-nav_android_home_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_home_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-nav_android_home_inactive.svg b/src/assets/images/ic-nav_android_home_inactive.svg new file mode 100644 index 00000000..a845b80f --- /dev/null +++ b/src/assets/images/ic-nav_android_home_inactive.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_home_active_black + Created with Sketch. + + + + + + + diff --git a/src/assets/images/ic-nav_android_multiapp_active.svg b/src/assets/images/ic-nav_android_multiapp_active.svg new file mode 100644 index 00000000..30a8036d --- /dev/null +++ b/src/assets/images/ic-nav_android_multiapp_active.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_multiapp_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-nav_android_multiapp_active_black.svg b/src/assets/images/ic-nav_android_multiapp_active_black.svg new file mode 100644 index 00000000..dec8656e --- /dev/null +++ b/src/assets/images/ic-nav_android_multiapp_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_multiapp_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-nav_android_multiapp_inactive.svg b/src/assets/images/ic-nav_android_multiapp_inactive.svg new file mode 100644 index 00000000..144bf498 --- /dev/null +++ b/src/assets/images/ic-nav_android_multiapp_inactive.svg @@ -0,0 +1,12 @@ + + + + ic-nav_android_multiapp_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-resolution_active.svg b/src/assets/images/ic-resolution_active.svg new file mode 100644 index 00000000..5c540135 --- /dev/null +++ b/src/assets/images/ic-resolution_active.svg @@ -0,0 +1,12 @@ + + + + ic-resolution_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-resolution_active_black.svg b/src/assets/images/ic-resolution_active_black.svg new file mode 100644 index 00000000..fe85a92f --- /dev/null +++ b/src/assets/images/ic-resolution_active_black.svg @@ -0,0 +1,12 @@ + + + + ic-resolution_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-resolution_inactive.svg b/src/assets/images/ic-resolution_inactive.svg new file mode 100644 index 00000000..1eb88ca5 --- /dev/null +++ b/src/assets/images/ic-resolution_inactive.svg @@ -0,0 +1,12 @@ + + + + ic-resolution_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-screencast_default.svg b/src/assets/images/ic-screencast_default.svg new file mode 100644 index 00000000..195bee77 --- /dev/null +++ b/src/assets/images/ic-screencast_default.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/src/assets/images/ic-screencast_disabled.svg b/src/assets/images/ic-screencast_disabled.svg new file mode 100644 index 00000000..bda60caa --- /dev/null +++ b/src/assets/images/ic-screencast_disabled.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/src/assets/images/ic-screenshot_active.svg b/src/assets/images/ic-screenshot_active.svg new file mode 100644 index 00000000..2e0c41c4 --- /dev/null +++ b/src/assets/images/ic-screenshot_active.svg @@ -0,0 +1,22 @@ + + + + ic-screenshot_active + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-screenshot_active_black.svg b/src/assets/images/ic-screenshot_active_black.svg new file mode 100644 index 00000000..316af4dd --- /dev/null +++ b/src/assets/images/ic-screenshot_active_black.svg @@ -0,0 +1,22 @@ + + + + ic-screenshot_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic-screenshot_default.svg b/src/assets/images/ic-screenshot_default.svg new file mode 100644 index 00000000..87444ae0 --- /dev/null +++ b/src/assets/images/ic-screenshot_default.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/src/assets/images/ic-screenshot_disabled.svg b/src/assets/images/ic-screenshot_disabled.svg new file mode 100644 index 00000000..e970f66b --- /dev/null +++ b/src/assets/images/ic-screenshot_disabled.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/src/assets/images/ic-screenshot_inactive.svg b/src/assets/images/ic-screenshot_inactive.svg new file mode 100644 index 00000000..3473b0c9 --- /dev/null +++ b/src/assets/images/ic-screenshot_inactive.svg @@ -0,0 +1,22 @@ + + + + ic-screenshot_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_arrow_rotation_left.svg b/src/assets/images/ic_arrow_rotation_left.svg new file mode 100644 index 00000000..e4e6dc9f --- /dev/null +++ b/src/assets/images/ic_arrow_rotation_left.svg @@ -0,0 +1,14 @@ + + + + ic_arrow_rotation_left + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_arrow_rotation_right.svg b/src/assets/images/ic_arrow_rotation_right.svg new file mode 100644 index 00000000..1ffe89a9 --- /dev/null +++ b/src/assets/images/ic_arrow_rotation_right.svg @@ -0,0 +1,14 @@ + + + + ic_arrow_rotation_right + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_baseband_active.svg b/src/assets/images/ic_baseband_active.svg new file mode 100644 index 00000000..6b680300 --- /dev/null +++ b/src/assets/images/ic_baseband_active.svg @@ -0,0 +1,12 @@ + + + + ic_baseband_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_baseband_active_black.svg b/src/assets/images/ic_baseband_active_black.svg new file mode 100644 index 00000000..c2ead57c --- /dev/null +++ b/src/assets/images/ic_baseband_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_baseband_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_baseband_inactive.svg b/src/assets/images/ic_baseband_inactive.svg new file mode 100644 index 00000000..88e9458a --- /dev/null +++ b/src/assets/images/ic_baseband_inactive.svg @@ -0,0 +1,12 @@ + + + + ic_baseband_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_camera_active.svg b/src/assets/images/ic_camera_active.svg new file mode 100644 index 00000000..e61face2 --- /dev/null +++ b/src/assets/images/ic_camera_active.svg @@ -0,0 +1,15 @@ + + + + ic_camera_active + Created with Sketch. + + + + + + + + + + diff --git a/src/assets/images/ic_camera_active_black.svg b/src/assets/images/ic_camera_active_black.svg new file mode 100644 index 00000000..3cea1253 --- /dev/null +++ b/src/assets/images/ic_camera_active_black.svg @@ -0,0 +1,15 @@ + + + + ic_camera_active_black + Created with Sketch. + + + + + + + + + + diff --git a/src/assets/images/ic_camera_inactive.svg b/src/assets/images/ic_camera_inactive.svg new file mode 100644 index 00000000..d8e541f3 --- /dev/null +++ b/src/assets/images/ic_camera_inactive.svg @@ -0,0 +1,15 @@ + + + + ic_camera_active + Created with Sketch. + + + + + + + + + + diff --git a/src/assets/images/ic_click_to_display_default.svg b/src/assets/images/ic_click_to_display_default.svg new file mode 100644 index 00000000..dee5fc26 --- /dev/null +++ b/src/assets/images/ic_click_to_display_default.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/ic_click_to_display_hover.svg b/src/assets/images/ic_click_to_display_hover.svg new file mode 100644 index 00000000..176f66d7 --- /dev/null +++ b/src/assets/images/ic_click_to_display_hover.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/ic_clipboard_active.svg b/src/assets/images/ic_clipboard_active.svg new file mode 100644 index 00000000..d0856f9e --- /dev/null +++ b/src/assets/images/ic_clipboard_active.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/ic_clipboard_inactive.svg b/src/assets/images/ic_clipboard_inactive.svg new file mode 100644 index 00000000..bc610c5a --- /dev/null +++ b/src/assets/images/ic_clipboard_inactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/ic_cloud_upload.svg b/src/assets/images/ic_cloud_upload.svg new file mode 100644 index 00000000..c7c8232e --- /dev/null +++ b/src/assets/images/ic_cloud_upload.svg @@ -0,0 +1,12 @@ + + + + +Group 5 +Created with Sketch. + + diff --git a/src/assets/images/ic_cloud_upload_congralutation.svg b/src/assets/images/ic_cloud_upload_congralutation.svg new file mode 100644 index 00000000..0ac066b9 --- /dev/null +++ b/src/assets/images/ic_cloud_upload_congralutation.svg @@ -0,0 +1,25 @@ + + + + +Group 5 +Created with Sketch. + + + + + + + + + + + + + + diff --git a/src/assets/images/ic_cloud_upload_failed.svg b/src/assets/images/ic_cloud_upload_failed.svg new file mode 100644 index 00000000..3a1e52e5 --- /dev/null +++ b/src/assets/images/ic_cloud_upload_failed.svg @@ -0,0 +1,23 @@ + + + + Group 2 + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_diskIO_active.svg b/src/assets/images/ic_diskIO_active.svg new file mode 100644 index 00000000..60333cc5 --- /dev/null +++ b/src/assets/images/ic_diskIO_active.svg @@ -0,0 +1,34 @@ + + + + ic_diskIO_active + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_diskIO_active_black.svg b/src/assets/images/ic_diskIO_active_black.svg new file mode 100644 index 00000000..c2dd0bd1 --- /dev/null +++ b/src/assets/images/ic_diskIO_active_black.svg @@ -0,0 +1,34 @@ + + + + ic_diskIO_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_diskIO_inactive.svg b/src/assets/images/ic_diskIO_inactive.svg new file mode 100644 index 00000000..88a63633 --- /dev/null +++ b/src/assets/images/ic_diskIO_inactive.svg @@ -0,0 +1,34 @@ + + + + ic_diskIO_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_error_default.svg b/src/assets/images/ic_error_default.svg new file mode 100644 index 00000000..3eb22a4c --- /dev/null +++ b/src/assets/images/ic_error_default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/ic_fullscreen_active.svg b/src/assets/images/ic_fullscreen_active.svg new file mode 100644 index 00000000..d6b58e16 --- /dev/null +++ b/src/assets/images/ic_fullscreen_active.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_active + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_fullscreen_active_black.svg b/src/assets/images/ic_fullscreen_active_black.svg new file mode 100644 index 00000000..0f143fc8 --- /dev/null +++ b/src/assets/images/ic_fullscreen_active_black.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_fullscreen_exit_active.svg b/src/assets/images/ic_fullscreen_exit_active.svg new file mode 100644 index 00000000..f556bfd9 --- /dev/null +++ b/src/assets/images/ic_fullscreen_exit_active.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_exit_active + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_fullscreen_exit_active_black.svg b/src/assets/images/ic_fullscreen_exit_active_black.svg new file mode 100644 index 00000000..3d6368f5 --- /dev/null +++ b/src/assets/images/ic_fullscreen_exit_active_black.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_exit_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/ic_fullscreen_exit_inactive.svg b/src/assets/images/ic_fullscreen_exit_inactive.svg new file mode 100644 index 00000000..a69ca357 --- /dev/null +++ b/src/assets/images/ic_fullscreen_exit_inactive.svg @@ -0,0 +1,51 @@ + + + + ic_fullscreen_exit_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_fullscreen_inactive.svg b/src/assets/images/ic_fullscreen_inactive.svg new file mode 100644 index 00000000..1ec2169b --- /dev/null +++ b/src/assets/images/ic_fullscreen_inactive.svg @@ -0,0 +1,39 @@ + + + + ic_fullscreen_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_icon_mute.svg b/src/assets/images/ic_icon_mute.svg new file mode 100644 index 00000000..d69accdd --- /dev/null +++ b/src/assets/images/ic_icon_mute.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/ic_icon_power_active.svg b/src/assets/images/ic_icon_power_active.svg new file mode 100644 index 00000000..5d0d8e1d --- /dev/null +++ b/src/assets/images/ic_icon_power_active.svg @@ -0,0 +1,12 @@ + + + + ic_icon_power_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_icon_power_inactive.svg b/src/assets/images/ic_icon_power_inactive.svg new file mode 100644 index 00000000..77718325 --- /dev/null +++ b/src/assets/images/ic_icon_power_inactive.svg @@ -0,0 +1,12 @@ + + + + ic_icon_power_inactive copy + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_icon_power_inactive_black.svg b/src/assets/images/ic_icon_power_inactive_black.svg new file mode 100644 index 00000000..bab7ae55 --- /dev/null +++ b/src/assets/images/ic_icon_power_inactive_black.svg @@ -0,0 +1,12 @@ + + + + ic_icon_power_inactive_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_id_active.svg b/src/assets/images/ic_id_active.svg new file mode 100644 index 00000000..ce09881f --- /dev/null +++ b/src/assets/images/ic_id_active.svg @@ -0,0 +1,23 @@ + + + + ic_id_active + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_id_active_black.svg b/src/assets/images/ic_id_active_black.svg new file mode 100644 index 00000000..6bd534a2 --- /dev/null +++ b/src/assets/images/ic_id_active_black.svg @@ -0,0 +1,23 @@ + + + + ic_id_active_black + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_id_inactive.svg b/src/assets/images/ic_id_inactive.svg new file mode 100644 index 00000000..9dd07af2 --- /dev/null +++ b/src/assets/images/ic_id_inactive.svg @@ -0,0 +1,23 @@ + + + + ic_id_inactive + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_installation_active.svg b/src/assets/images/ic_installation_active.svg new file mode 100644 index 00000000..f046c268 --- /dev/null +++ b/src/assets/images/ic_installation_active.svg @@ -0,0 +1,12 @@ + + + + ic_installation_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_installation_active_black.svg b/src/assets/images/ic_installation_active_black.svg new file mode 100644 index 00000000..20ea5a9b --- /dev/null +++ b/src/assets/images/ic_installation_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_installation_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_installation_inactive.svg b/src/assets/images/ic_installation_inactive.svg new file mode 100644 index 00000000..f87c8bf5 --- /dev/null +++ b/src/assets/images/ic_installation_inactive.svg @@ -0,0 +1,12 @@ + + + + ic_installation_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_location_active.svg b/src/assets/images/ic_location_active.svg new file mode 100644 index 00000000..11648f1e --- /dev/null +++ b/src/assets/images/ic_location_active.svg @@ -0,0 +1,12 @@ + + + + ic_location_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_location_active_black.svg b/src/assets/images/ic_location_active_black.svg new file mode 100644 index 00000000..dcb3fd8e --- /dev/null +++ b/src/assets/images/ic_location_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_location_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_location_inactive.svg b/src/assets/images/ic_location_inactive.svg new file mode 100644 index 00000000..fc256d49 --- /dev/null +++ b/src/assets/images/ic_location_inactive.svg @@ -0,0 +1,12 @@ + + + + ic_location_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_more_active.svg b/src/assets/images/ic_more_active.svg new file mode 100644 index 00000000..75492613 --- /dev/null +++ b/src/assets/images/ic_more_active.svg @@ -0,0 +1,15 @@ + + + + ic_more_active + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_more_active_black.svg b/src/assets/images/ic_more_active_black.svg new file mode 100644 index 00000000..fdaacd2a --- /dev/null +++ b/src/assets/images/ic_more_active_black.svg @@ -0,0 +1,15 @@ + + + + ic_more_active_black + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_more_inactive.svg b/src/assets/images/ic_more_inactive.svg new file mode 100644 index 00000000..7a7e6628 --- /dev/null +++ b/src/assets/images/ic_more_inactive.svg @@ -0,0 +1,15 @@ + + + + ic_more_inactive + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_network_active.svg b/src/assets/images/ic_network_active.svg new file mode 100644 index 00000000..1e3df195 --- /dev/null +++ b/src/assets/images/ic_network_active.svg @@ -0,0 +1,12 @@ + + + + ic_network_active + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_network_active_black.svg b/src/assets/images/ic_network_active_black.svg new file mode 100644 index 00000000..e44af977 --- /dev/null +++ b/src/assets/images/ic_network_active_black.svg @@ -0,0 +1,12 @@ + + + + ic_network_active_black + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_network_inactive.svg b/src/assets/images/ic_network_inactive.svg new file mode 100644 index 00000000..d49b6c3b --- /dev/null +++ b/src/assets/images/ic_network_inactive.svg @@ -0,0 +1,12 @@ + + + + ic_network_inactive + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_pixelperfect_active.svg b/src/assets/images/ic_pixelperfect_active.svg new file mode 100644 index 00000000..a5e8654a --- /dev/null +++ b/src/assets/images/ic_pixelperfect_active.svg @@ -0,0 +1,18 @@ + + + + ic_pixelperfect_active + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_pixelperfect_active_black.svg b/src/assets/images/ic_pixelperfect_active_black.svg new file mode 100644 index 00000000..59235641 --- /dev/null +++ b/src/assets/images/ic_pixelperfect_active_black.svg @@ -0,0 +1,18 @@ + + + + ic_pixelperfect_active_black + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_pixelperfect_inactive.svg b/src/assets/images/ic_pixelperfect_inactive.svg new file mode 100644 index 00000000..70398ab2 --- /dev/null +++ b/src/assets/images/ic_pixelperfect_inactive.svg @@ -0,0 +1,18 @@ + + + + ic_pixelperfect_inactive + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_rotation_active.svg b/src/assets/images/ic_rotation_active.svg new file mode 100644 index 00000000..141f0602 --- /dev/null +++ b/src/assets/images/ic_rotation_active.svg @@ -0,0 +1,14 @@ + + + + ic_rotation_active + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_rotation_active_black.svg b/src/assets/images/ic_rotation_active_black.svg new file mode 100644 index 00000000..f82037b9 --- /dev/null +++ b/src/assets/images/ic_rotation_active_black.svg @@ -0,0 +1,14 @@ + + + + ic_rotation_active_black + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_rotation_inactive.svg b/src/assets/images/ic_rotation_inactive.svg new file mode 100644 index 00000000..f78521b9 --- /dev/null +++ b/src/assets/images/ic_rotation_inactive.svg @@ -0,0 +1,14 @@ + + + + ic_rotation_inactive + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_active.svg b/src/assets/images/ic_sound_active.svg new file mode 100644 index 00000000..7c6be83c --- /dev/null +++ b/src/assets/images/ic_sound_active.svg @@ -0,0 +1,14 @@ + + + + ic_sound_active + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_active_black.svg b/src/assets/images/ic_sound_active_black.svg new file mode 100644 index 00000000..eb53593b --- /dev/null +++ b/src/assets/images/ic_sound_active_black.svg @@ -0,0 +1,14 @@ + + + + ic_sound_active_black + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_down_active.svg b/src/assets/images/ic_sound_down_active.svg new file mode 100644 index 00000000..a45012b0 --- /dev/null +++ b/src/assets/images/ic_sound_down_active.svg @@ -0,0 +1,15 @@ + + + + ic_sound_down_active + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_down_inactive.svg b/src/assets/images/ic_sound_down_inactive.svg new file mode 100644 index 00000000..66d50cb6 --- /dev/null +++ b/src/assets/images/ic_sound_down_inactive.svg @@ -0,0 +1,15 @@ + + + + ic_sound_down_inactive + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_inactive.svg b/src/assets/images/ic_sound_inactive.svg new file mode 100644 index 00000000..eb686c09 --- /dev/null +++ b/src/assets/images/ic_sound_inactive.svg @@ -0,0 +1,14 @@ + + + + ic_sound_inactive + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_up_active.svg b/src/assets/images/ic_sound_up_active.svg new file mode 100644 index 00000000..6e70ee69 --- /dev/null +++ b/src/assets/images/ic_sound_up_active.svg @@ -0,0 +1,16 @@ + + + + ic_sound_up_active + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_sound_up_inactive.svg b/src/assets/images/ic_sound_up_inactive.svg new file mode 100644 index 00000000..933ed079 --- /dev/null +++ b/src/assets/images/ic_sound_up_inactive.svg @@ -0,0 +1,16 @@ + + + + ic_sound_up_inactive + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_textandcall_active.svg b/src/assets/images/ic_textandcall_active.svg new file mode 100644 index 00000000..bfb5fcc1 --- /dev/null +++ b/src/assets/images/ic_textandcall_active.svg @@ -0,0 +1,15 @@ + + + + ic_textandcall_active + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_textandcall_active_black.svg b/src/assets/images/ic_textandcall_active_black.svg new file mode 100644 index 00000000..cac61b47 --- /dev/null +++ b/src/assets/images/ic_textandcall_active_black.svg @@ -0,0 +1,15 @@ + + + + ic_textandcall_active_black + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_textandcall_inactive.svg b/src/assets/images/ic_textandcall_inactive.svg new file mode 100644 index 00000000..c9b468d7 --- /dev/null +++ b/src/assets/images/ic_textandcall_inactive.svg @@ -0,0 +1,13 @@ + + + + ic_textandcall_inactive + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/ic_warning.svg b/src/assets/images/ic_warning.svg new file mode 100644 index 00000000..608d77ad --- /dev/null +++ b/src/assets/images/ic_warning.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/loader.svg b/src/assets/images/loader.svg new file mode 100644 index 00000000..93c34043 --- /dev/null +++ b/src/assets/images/loader.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/assets/images/spinner-material.svg b/src/assets/images/spinner-material.svg new file mode 100644 index 00000000..8d66bdd1 --- /dev/null +++ b/src/assets/images/spinner-material.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/plugins/Battery.js b/src/plugins/Battery.js new file mode 100644 index 00000000..3a4fb880 --- /dev/null +++ b/src/plugins/Battery.js @@ -0,0 +1,280 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +/** + * Instance battery plugin. + * Provides battery level and state control. + */ +module.exports = class Battery extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.battery = this; + + // Listen for initial battery charging status + this.instance.registerEventCallback('BATTERY_STATUS', (value) => { + this.updateUIBatteryChargingState(value !== 'false'); + }); + + // Display widget + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for initial battery level + this.instance.registerEventCallback('BATTERY_LEVEL', this.onBatteryLevelChange.bind(this)); + + // Listen for battery messages: "state mode " + this.instance.registerEventCallback('battery', (message) => { + const values = message.split(' ').splice(-2); + + if (values.length !== 2) { + return; + } + + this.updateUIBatteryChargingState(values[0] !== 'discharging'); + this.onBatteryLevelChange(values[1]); + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-battery-button'; + this.toolbarBtnImage.title = this.i18n.BATTERY_TITLE || 'Battery'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.container = document.createElement('div'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.BATTERY_TITLE || 'Battery'; + this.container.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + const batteryLevelLabel = this.i18n.BATTERY_CHARGE_LEVEL || 'Charge level'; + inputs.innerHTML = ''; + + // Create charge level inputs + inputs.appendChild(this.createLevelSection()); + + // Add charging label + const chargingLabel = document.createElement('label'); + chargingLabel.innerHTML = this.i18n.BATTERY_CHARGE_STATE || 'State of charge'; + inputs.appendChild(chargingLabel); + + // Add charging section + inputs.appendChild(this.createChargingSection()); + + // Setup + this.container.appendChild(inputs); + this.widget.className = 'gm-overlay gm-battery-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.onclick = this.toggleWidget.bind(this); + close.className = 'gm-close-btn'; + + this.widget.appendChild(close); + this.widget.appendChild(this.container); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Create and return the widget "charging" section. + * + * @return {HTMLElement} Charging section. + */ + createChargingSection() { + const chargingGroup = document.createElement('div'); + this.chargingInput = document.createElement('input'); + this.chargingStatus = document.createElement('div'); + chargingGroup.className = 'gm-charging-group'; + this.chargingInput.type = 'checkbox'; + this.chargingInput.className = 'gm-charging-checkbox'; + this.chargingInput.onchange = this.toggleChargingState.bind(this); + this.chargingInput.checked = true; + this.chargingStatus.className = 'gm-charging-status'; + this.chargingStatus.innerHTML = 'Discharging'; + chargingGroup.appendChild(this.chargingInput); + chargingGroup.appendChild(this.chargingStatus); + + return chargingGroup; + } + + /** + * Create and return the widget "level" section. + * + * @return {HTMLElement} Level section. + */ + createLevelSection() { + const chargeGroup = document.createElement('div'); + chargeGroup.className = 'gm-charge-level-group'; + + const inputGroup = document.createElement('div'); + this.chargeInput = document.createElement('input'); + const chargeInputLabel = document.createElement('span'); + chargeInputLabel.innerHTML = '%'; + this.chargeInput.className = 'gm-charge-input'; + this.chargeInput.type = 'number'; + this.chargeInput.value = 50; + this.chargeInput.max = 100; + this.chargeInput.min = 0; + this.chargeInput.step = 1; + this.chargeInput.oninput = this.onBatteryLevelChangeEvent.bind(this); + inputGroup.appendChild(this.chargeInput); + inputGroup.appendChild(chargeInputLabel); + chargeGroup.appendChild(inputGroup); + + const sliderGroup = document.createElement('div'); + this.chargeSlider = document.createElement('input'); + this.chargeSlider.className = 'gm-charge-slider'; + this.chargeSlider.type = 'range'; + this.chargeSlider.orient = 'vertical'; + this.chargeSlider.max = 100; + this.chargeSlider.min = 0; + this.chargeSlider.step = 1; + this.chargeSlider.value = 50; + this.chargeSlider.onchange = this.onBatteryLevelChangeEvent.bind(this); + sliderGroup.appendChild(this.chargeSlider); + chargeGroup.appendChild(sliderGroup); + + const chargeImageGroup = document.createElement('div'); + this.chargeImage = document.createElement('div'); + this.chargeImageOverlay = document.createElement('div'); + this.chargeImage.className = 'gm-charge-image'; + this.chargeImageOverlay.className = 'gm-charge-image-overlay'; + this.chargeImageOverlay.style.cssText = 'height: ' + 40 + '%;'; + chargeImageGroup.appendChild(this.chargeImage); + this.chargeImage.appendChild(this.chargeImageOverlay); + chargeGroup.appendChild(chargeImageGroup); + + return chargeGroup; + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Toggle Instance charging status between 'charging' and 'discharging'; + */ + toggleChargingState() { + this.chargingStatus.classList.toggle('charging'); + + const chargingLabel = this.i18n.BATTERY_CHARGING || 'Charging'; + const dischargingLabel = this.i18n.BATTERY_DISCHARGING || 'Discharging'; + + this.chargingStatus.innerHTML = this.chargingInput.checked ? chargingLabel : dischargingLabel; + this.sendDataToInstance(); + } + + /** + * Update widget charging state UI. + * + * @param {boolean} charging Whether or not the battery is charging. + */ + updateUIBatteryChargingState(charging) { + this.chargingInput.checked = charging; + this.chargingStatus.classList[charging ? 'add' : 'remove']('charging'); + + const chargingLabel = this.i18n.BATTERY_CHARGING || 'Charging'; + const dischargingLabel = this.i18n.BATTERY_DISCHARGING || 'Discharging'; + + this.chargingStatus.innerHTML = charging ? chargingLabel : dischargingLabel; + } + + /** + * Handle battery level change events (slider & text input). + * + * @param {Event} event Event. + */ + onBatteryLevelChangeEvent(event) { + const value = Number(event.target.value); + if (this.onBatteryLevelChange(value)) { + this.sendDataToInstance(); + } + } + + /** + * Synchronize widget UI. + * + * @param {number} value Battery level. + * @return {boolean} Whether or not battery level has been applied. + */ + onBatteryLevelChange(value) { + value = Number(value); + if (Number.isNaN(value)) { + return false; + } + + value = Math.min(Math.max(0, value), 100); + this.chargeSlider.value = value; + this.chargeInput.value = value; + this.chargeImageOverlay.style.cssText = 'height: ' + (value * 0.7 + 4) + '%;'; + return true; + } + + /** + * Send information to instance. + */ + sendDataToInstance() { + const level = Number(this.chargeInput.value); + const charging = this.chargingInput.checked ? 'charging' : 'discharging'; + const json = { + channel: 'battery', messages: [ + 'set state level ' + level, + 'set state status ' + charging, + ], + }; + this.instance.sendEvent(json); + } +}; diff --git a/src/plugins/ButtonsEvents.js b/src/plugins/ButtonsEvents.js new file mode 100644 index 00000000..b06b58ab --- /dev/null +++ b/src/plugins/ButtonsEvents.js @@ -0,0 +1,230 @@ +'use strict'; + +const META_KEYCODE = '0x01000022'; +const ENTER_KEYCODE = '0x01000005'; +const VOLUME_DOWN_KEYCODE = '0x01000070'; +const VOLUME_UP_KEYCODE = '0x01000072'; +const RECENT_APP_KEYCODE = '0x010000be'; +const HOME_KEYCODE = '0x01000010'; +const BACK_KEYCODE = '0x01000061'; +const POWER_KEYCODE = '0x0100010b'; +const ROTATE_KEYCODE = 'gm-rotation'; + +/** + * Instance physical buttons plugin. + * Provides physical buttons (power, volume, ...) control. + */ +module.exports = class ButtonsEvents { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + * @param {Object} translateHomeKey Translate HOME key press for the instance. + */ + constructor(instance, i18n, translateHomeKey) { + // Reference instance + this.instance = instance; + this.translateHomeKey = translateHomeKey; + + // Register plugin + this.instance.buttonsEvents = this; + + if (this.instance.options.rotation) { + this.renderToolbarButton(ROTATE_KEYCODE, 'gm-rotate', i18n.BUTTONS_ROTATE || 'Rotate', false); + } + + if (this.instance.options.volume) { + this.renderToolbarButton( + VOLUME_DOWN_KEYCODE, 'gm-sound-down', i18n.BUTTONS_SOUND_DOWN || 'Sound down', false + ); + this.renderToolbarButton(VOLUME_UP_KEYCODE, 'gm-sound-up', i18n.BUTTONS_SOUND_UP || 'Sound up', false); + } + + if (this.instance.options.navbar) { + this.renderToolbarButton( + RECENT_APP_KEYCODE, 'gm-recent', i18n.BUTTONS_RECENT_APPS || 'Recent applications', true + ); + this.renderToolbarButton(HOME_KEYCODE, 'gm-home', i18n.BUTTONS_HOME || 'Home', true); + this.renderToolbarButton(BACK_KEYCODE, 'gm-back', i18n.BUTTONS_BACK || 'Back', true); + } + + if (this.instance.options.power) { + this.renderToolbarButton(POWER_KEYCODE, 'gm-power', i18n.BUTTONS_POWER || 'Power', true); + } + + const buttons = document.getElementsByClassName('gm-icon-button'); + for (let i = 0; i < buttons.length; i++) { + const button = buttons[i]; + button.onmousedown = this.mouseButtonPressEvent.bind(this); + button.onmouseup = this.mouseButtonReleaseEvent.bind(this); + } + } + + /** + * Called on keyboard keypress event. + * Relay keypress event to the instance. + * + * @param {string} key Pressed key unicode reference number. + * @param {string} text Pressed key character. + */ + keyPressEvent(key, text) { + const json = {type: 'KEYBOARD_PRESS', keychar: text, keycode: key}; + this.instance.sendEvent(json); + } + + /** + * Called on keyboard keyrelease event. + * Relay keyrelease event to the instance. + * + * @param {string} key Released key unicode reference number. + * @param {string} text Released key character. + */ + keyReleaseEvent(key, text) { + const json = {type: 'KEYBOARD_RELEASE', keychar: text, keycode: key}; + this.instance.sendEvent(json); + } + + /** + * Called on mouse down event. + * Relay event to the instance. + * + * @param {Event} event Associated JS event + */ + mouseButtonPressEvent(event) { + const id = event.target.classList[1]; + if (!id) { + return; + } + + if (id === HOME_KEYCODE && this.translateHomeKey) { // home is meta + enter + this.keyPressEvent(parseInt(META_KEYCODE), ''); + this.keyPressEvent(parseInt(ENTER_KEYCODE), ''); + } else if (id.substring(0, 2) === '0x') { // else if it is a raw key we send it + const key = parseInt(id, 16); + this.keyPressEvent(key, '0\n'); + } + } + + /** + * Called on mouse up event. + * Relay event to the instance. + * + * @param {Event} event Associated JS event + */ + mouseButtonReleaseEvent(event) { + const id = event.target.classList[1]; + if (!id) { + return; + } + + if (id === HOME_KEYCODE && this.translateHomeKey) { // "home" is "meta + enter" on PaaS, "move_home" on SaaS + this.keyReleaseEvent(parseInt(ENTER_KEYCODE), ''); + this.keyReleaseEvent(parseInt(META_KEYCODE), ''); + } else if (id === ROTATE_KEYCODE) { // rotate is a custom command + const json = {type: 'ROTATE'}; + this.instance.sendEvent(json); + } else if (id.substring(0, 2) === '0x') { // else if it is a raw key we send it + const key = parseInt(id, 16); + this.keyReleaseEvent(key, '0\n'); + } + } + + /** + * Retrieve a button from its class name. + * + * @param {string} className Button class name. + * @return {Object} Button state. + */ + getButtonState(className) { + const image = this.instance.getChildByClass(this.instance.root, className); + const button = image.parentNode; + + return { + image: { + className: image.className, + }, + button: { + className: button.className, + onclick: button.onclick, + } + }; + } + + /** + * Retrieve a button state identified by its class name. + * + * @param {string} className Button class name. + * @param {Object} state Button state + */ + setButtonState(className, state) { + const image = this.instance.getChildByClass(this.instance.root, className); + const button = image.parentNode; + + image.className = state.image.className; + button.className = state.button.className; + button.onclick = state.button.onclick; + } + + /** + * Disable instance rotation. + */ + disableRotation() { + if (!this.instance.getChildByClass(this.instance.root, 'gm-rotate')) { + return; // if we don't have rotation, we can't disable it + } + + if (!this.savedRotationState) { + this.savedRotationState = this.getButtonState('gm-rotate'); + } + + const rotationImage = this.instance.getChildByClass(this.instance.root, 'gm-rotate'); + const rotationButton = rotationImage.parentNode; + rotationButton.className += ' gm-disabled-widget-pop-up'; + rotationImage.className += ' gm-disabled-widget-icon'; + rotationButton.onclick = null; + } + + /** + * Enable instance rotation. + */ + enableRotation() { + if (!this.instance.getChildByClass(this.instance.root, 'gm-rotate')) { + return; // if we don't have rotation, we can't disable it + } + + if (this.savedRotationState) { + this.setButtonState('gm-rotate', this.savedRotationState); + this.savedRotationState = null; + } + } + + /** + * Add a device button to the player toolbar. + * + * @param {string} androidKeycode Button keycode. + * @param {string} htmlClass Button CSS class. + * @param {string} htmlTitle Button title. + * @param {boolean} append Set to true to insert the button at the end. + */ + renderToolbarButton(androidKeycode, htmlClass, htmlTitle, append) { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + const button = document.createElement('li'); + const image = document.createElement('div'); + image.classList.add('gm-icon-button'); + image.classList.add(androidKeycode); + image.classList.add(htmlClass); + image.title = htmlTitle; + button.appendChild(image); + if (append) { + toolbar.appendChild(button); + } else { + toolbar.insertBefore(button, toolbar.firstChild); + } + } +}; diff --git a/src/plugins/Camera.js b/src/plugins/Camera.js new file mode 100644 index 00000000..ad77e4c5 --- /dev/null +++ b/src/plugins/Camera.js @@ -0,0 +1,127 @@ +'use strict'; + +const adapterjs = require('webrtc-adapter'); +const OverlayPlugin = require('./util/OverlayPlugin'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +/** + * Instance camera plugin. + * Provides client webcam and camera control. + */ +module.exports = class Camera extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.camera = this; + + this.streaming = false; + + // Display widget + this.renderToolbarButton(); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-camera-button'; + this.toolbarBtnImage.title = this.i18n.CAMERA_TITLE || 'Use webcam as camera'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + toolbar.appendChild(this.toolbarBtn); + + // if we are using safari, we disable camera since it sends its stream on h264 which is not currently supported + if (adapterjs.default.browserDetails.browser === 'safari') { + this.toolbarBtn.className += ' gm-disabled-widget-pop-up'; + this.toolbarBtnImage.className += ' gm-disabled-widget-icon'; + } else { + this.toolbarBtn.onclick = this.toggleStreaming.bind(this); + } + } + + /** + * Toggle local video forward forwarding. + * Redirect the client webcam video stream to the instance. + */ + toggleStreaming() { + if (!this.streaming) { + this.startStreaming(); + } else { + this.stopStreaming(); + } + } + + /** + * Initialize and start client webcam video stream. + */ + startStreaming() { + if (!navigator.mediaDevices) { + return; + } + + navigator.mediaDevices.getUserMedia({ + audio: this.instance.options.microphone, + video: { + width:1280, + height: 720, + }, + }) + .catch((err) => { + this.onVideoStreamError(err); + }) + .then((mediaStream) => { + log.debug('Client video stream ready'); + this.streaming = true; + this.localStream = mediaStream; + this.instance.addLocalStream(mediaStream); + }); + } + + /** + * Client video stream error handler. + * + * @param {Error} error Camera stream error. + */ + onVideoStreamError(error) { + log.warn('Can\'t start client video stream', error); + } + + /** + * Stop client webcam video stream. + */ + stopStreaming() { + log.debug('removed local stream'); + this.instance.removeLocalStream(this.localStream); + this.localStream = null; + this.streaming = false; + } + + /** + * Get client webcam video stream. + * + * @return {MediaStream} Client video stream + */ + getClientVideoStream() { + return this.localStream; + } +}; diff --git a/src/plugins/Clipboard.js b/src/plugins/Clipboard.js new file mode 100644 index 00000000..78bd1696 --- /dev/null +++ b/src/plugins/Clipboard.js @@ -0,0 +1,180 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +/** + * Instance clipboard plugin. + * Provides clipboard data exchange capability between client and instance. + */ +module.exports = class Clipboard extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.clipboard = this; + + // clipboard text + this.clipboard = ''; + + // Display widget + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for framework messages: "clipboard " + this.instance.registerEventCallback('framework', (message) => { + const values = message.split(' '); + if (values.length !== 3 || values[0] !== 'clipboard') { + return; + } + + try { + this.clipboard = decodeURIComponent(escape(window.atob(values[2]))); + } catch (error) { + log.warn('Malformed clipboard content'); + } + }); + + // Listen for initial clipboard status + this.instance.registerEventCallback('CLIPBOARD', (text) => { + this.clipboard = text; + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-clipboard-button'; + this.toolbarBtnImage.title = this.i18n.CLIPBOARD_TITLE || 'Clipboard'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.container = document.createElement('div'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.CLIPBOARD_TITLE || 'Clipboard'; + this.container.appendChild(title); + + this.clipboardInput = document.createElement('textarea'); + this.clipboardInput.className = 'gm-clipboard-input'; + + this.copiedText = document.createElement('div'); + this.copiedText.innerHTML = this.i18n.CLIPBOARD_COPIED || 'Copied to local clipboard'; + this.copiedText.classList.add('gm-text-copied'); + this.copiedText.classList.add('gm-invisible'); + this.copiedText.classList.add('gm-hidden'); + + // Setup + this.container.appendChild(this.clipboardInput); + this.container.appendChild(this.copiedText); + this.widget.className = 'gm-overlay gm-clipboard-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.container); + + this.widget.onclose = () => { + this.clipboard = this.clipboardInput.value; + this.sendDataToInstance(); + }; + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + this.clipboardInput.value = this.clipboard; + if (this.clipboard !== '') { + setTimeout(this.copyInstanceClipboardToClient, 100, this.clipboardInput, this.copiedText); + } + } else { + this.instance.emit('keyboard-enable'); + this.widget.onclose(); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Copy the instance clipboard (reflected to the text input field) into the client clipboard. + * + * @param {HTMLElement} clipboardInput Clipboard value (text input). + * @param {HTMLElement} clipboardCopyIndicator Clipboard copy indicator. + */ + copyInstanceClipboardToClient(clipboardInput, clipboardCopyIndicator) { + try { + navigator.clipboard.writeText(this.clipboardInput.value); + } catch (error) { + // Old, deprecated fallback + clipboardInput.focus(); + clipboardInput.select(); + document.execCommand('copy'); + } + + clipboardCopyIndicator.classList.remove('gm-invisible'); + clipboardCopyIndicator.classList.remove('gm-hidden'); + setTimeout(() => { + clipboardCopyIndicator.classList.add('gm-invisible'); + setTimeout(() => { + clipboardCopyIndicator.classList.add('gm-hidden'); + }, 500); + }, 2000); + } + + /** + * Send information to instance. + */ + sendDataToInstance() { + const json = { + channel: 'framework', messages: [ + 'set_device_clipboard ' + window.btoa(unescape(encodeURIComponent(this.clipboard))), + ], + }; + this.instance.sendEvent(json); + } +}; diff --git a/src/plugins/CoordinateUtils.js b/src/plugins/CoordinateUtils.js new file mode 100644 index 00000000..57311575 --- /dev/null +++ b/src/plugins/CoordinateUtils.js @@ -0,0 +1,165 @@ +'use strict'; + +/** + * Screen coordinates utility plugin. + * Provides utility functions related to mouse inputs & the instance video stream. + * Used by MouseEvents and MultiTouchEvents plugins. + */ +module.exports = class CoordinateUtils { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + */ + constructor(instance) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.coordinateUtils = this; + + // Virtual device video stream + this.video = this.instance.video; + } + + /** + * Keep the video stream reference in sync. + */ + setVideoElement() { + this.video = this.instance.video; + } + + /** + * Determine the position of the instance offset borders. + * + * @return {boolean} True if borders are on on the left and right side, else if they are on top and bottom. + */ + hasLeftAndRightBorders() { + return this.video.offsetWidth / this.video.offsetHeight > this.video.videoWidth / this.video.videoHeight; + } + + /** + * Determine if the instance and the player video share the same orientation. + * + * @return {boolean} True if the instance and the player video are in the same orientation. + */ + hasSameOrientation() { + return this.video.videoWidth / this.video.videoHeight > 1 && + this.video.offsetWidth / this.video.offsetHeight > 1 + || + this.video.videoWidth / this.video.videoHeight < 1 && + this.video.offsetWidth / this.video.offsetHeight < 1; + } + + /** + * Get cumulative offset between a givem element and the top left corner of the web page. + * + * @param {HTMLElement} element DOM element to get the position from. + * @return {Object} Offset (top and left). + */ + getCumulativeOffset(element) { + let top = 0; + let left = 0; + do { + top += element.offsetTop || 0; + left += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + + return { + top: top, + left: left, + }; + } + + /** + * Get the relative (cumulative, offset deduced) x coordinate from an event (touch or mouse). + * + * @param {Event} event Raw mouse/touch event. + * @return {number} Relative x coordinate. + */ + getRawXCoordinateFromEvent(event) { + if (typeof event.pageX !== 'undefined') { + if (typeof event.offsetX !== 'undefined') { + return event.offsetX; + } + return event.pageX - this.getCumulativeOffset(this.video).left; + } + return event.clientX - this.getCumulativeOffset(this.video).left; + } + + /** + * Get the relative (cumulative, offset deduced) y coordinate from an event (touch or mouse). + * + * @param {Event} event Raw mouse/touch event. + * @return {number} Relative y coordinate. + */ + getRawYCoordinateFromEvent(event) { + if (typeof event.pageY !== 'undefined') { + if (typeof event.offsetY !== 'undefined') { + return event.offsetY; + } + return event.pageY - this.getCumulativeOffset(this.video).top; + } + return event.clientY - this.getCumulativeOffset(this.video).top; + } + + /** + * Get the final emulated x coordinate. Video ratio & orientation taken into account. + * + * @param {Event} event Raw mouse/touch event. + * @return {number} Relative x coordinate. + */ + getXCoordinate(event) { + this.instance.root.focus(); // not on the Y function because we need to focus element only once + this.setVideoElement(); + const x = this.getRawXCoordinateFromEvent(event); + let videoRealSizeX, ratio; + if (this.hasSameOrientation()) { + if (this.hasLeftAndRightBorders()) { + videoRealSizeX = this.video.videoWidth / this.video.videoHeight * this.video.offsetHeight; + const xRatio = this.video.videoWidth / videoRealSizeX; + const blackBorderSize = (this.video.offsetWidth - videoRealSizeX) / 2; + return (x - blackBorderSize) * xRatio; + } + return this.video.videoWidth / this.video.offsetWidth * x; + } + if (this.hasLeftAndRightBorders()) { + videoRealSizeX = this.video.videoWidth / this.video.videoHeight * this.video.offsetHeight; + ratio = this.video.videoWidth / videoRealSizeX; + return (x - (this.video.offsetWidth - videoRealSizeX) / 2) * ratio; + } + const videoRealSizeY = this.video.videoHeight / this.video.videoWidth * this.video.offsetWidth; + ratio = this.video.videoHeight / videoRealSizeY; + return x * ratio; + } + + /** + * Get the final emulated y coordinate. Video ratio & orientation taken into account. + * + * @param {Event} event Raw mouse/touch event. + * @return {number} Relative y coordinate. + */ + getYCoordinate(event) { + this.setVideoElement(); + const y = this.getRawYCoordinateFromEvent(event); + let videoRealSizeX, videoRealSizeY, ratio; + if (this.hasSameOrientation()) { + if (this.hasLeftAndRightBorders()) { + return this.video.videoHeight / this.video.offsetHeight * y; + } + videoRealSizeY = this.video.videoHeight / this.video.videoWidth * this.video.offsetWidth; + const yRatio = this.video.videoHeight / videoRealSizeY; + const blackBorderTopSize = (this.video.offsetHeight - videoRealSizeY) / 2; + return (y - blackBorderTopSize) * yRatio; + } + if (this.hasLeftAndRightBorders()) { + videoRealSizeX = this.video.videoWidth / this.video.videoHeight * this.video.offsetHeight; + ratio = this.video.videoWidth / videoRealSizeX; + return y * ratio; + } + videoRealSizeY = this.video.videoHeight / this.video.videoWidth * this.video.offsetWidth; + ratio = this.video.videoHeight / videoRealSizeY; + return (y - (this.video.offsetHeight - videoRealSizeY) / 2) * ratio; + } +}; diff --git a/src/plugins/FileUpload.js b/src/plugins/FileUpload.js new file mode 100644 index 00000000..b265dbd5 --- /dev/null +++ b/src/plugins/FileUpload.js @@ -0,0 +1,777 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const LEFT = 0; +const RIGHT = 1; + +const CIRC = Math.PI * 2; +const QUART = Math.PI / 2; + +/** + * Instance file upload plugin. + * Provides file upload support and Open GApps installation control. + */ +module.exports = class FileUpload extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.fileUpload = this; + + this.root = this.instance.videoWrapper; + this.stepper = {}; + this.currentStep = 'homeScreen'; + this.isUploading = false; + this.hasError = false; + this.flashing = false; + this.canvasContext = null; + this.opengappsInstalled = false; + this.capabilityAvailable = false; + + if (window.Worker) { + let fileUploaderWorker = require('../worker/FileUploaderWorker'); + fileUploaderWorker = fileUploaderWorker.toString() + .match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]; + const src = new Blob([fileUploaderWorker], {type: 'application/javascript'}); + this.loaderWorker = new Worker(URL.createObjectURL(src)); + this.loaderWorker.onmessage = (event) => { + const msg = event.data; + switch (msg.code) { + case 'SUCCESS': + this.onUploadSuccess(); + break; + case 'FAIL': + this.onUploadFailure(); + break; + case 'PROGRESS': + this.setUploadProgress(msg.value); + break; + default: + break; + } + }; + } + + this.root.ondragenter = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + + this.root.ondragover = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + + this.root.ondragleave = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + + this.root.ondrop = (event) => { + event.stopPropagation(); + event.preventDefault(); + if (!this.isUploading) { + this.upload(event.dataTransfer.files); + } + }; + + // create stepper + this.createStepper(); + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for events + this.instance.registerEventCallback('SYSTEM_PATCHER_STATUS', this.onSystemPatcherStatusEvent.bind(this)); + this.instance.registerEventCallback( + 'SYSTEM_PATCHER_LAST_RESULT', this.onSystemPatcherLastResultEvent.bind(this) + ); + + /* + * Listen for systempatcher messages: "status " + * or "last_result _error " + */ + this.instance.registerEventCallback('systempatcher', (message) => { + const split = message.split(/ (.+)/); + if (split[0] === 'status') { + this.onSystemPatcherStatusEvent(split[1]); + } else if (split[0] === 'last_result') { + this.onSystemPatcherLastResultEvent(split[1]); + } + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + const button = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-uploader-button'; + this.toolbarBtnImage.title = this.i18n.UPLOADER_TITLE || 'File upload'; + button.appendChild(this.toolbarBtnImage); + button.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(button); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.container = document.createElement('div'); + + // Generate title + this.title = document.createElement('div'); + this.title.className = 'gm-upload-title'; + this.container.appendChild(this.title); + + // Generate input rows + this.inputs = document.createElement('div'); + this.inputs.className = 'gm-uploader-content'; + + // Setup + this.container.appendChild(this.inputs); + this.widget.className = 'gm-overlay gm-uploader-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + this.widget.appendChild(close); + this.widget.appendChild(this.container); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + this.displayStep(this.currentStep); + // Force refresh upload screen content. Otherwise, installing status may not appear. + if (this.currentStep === 'uploadScreen') { + const json = { + channel: 'systempatcher', messages: [ + 'notify status' + ] + }; + this.instance.sendEvent(json); + } + } else { + // Only reset step to homeScreen if we are in success or error step + if (this.currentStep === 'successScreen' || this.currentStep === 'errorScreen') { + this.currentStep = 'homeScreen'; + } + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Start uploading file(s) to the instance. + * + * @param {DataTransferItemList} files Files to upload. + */ + upload(files) { + if (files.length <= 0) { + return; + } + + this.createUploadProgressCanvas(); + this.setUploadProgress(0); + const msg = {type: 'upload', file: files[0]}; + this.loaderWorker.postMessage(msg); + this.isUploading = true; + this.hasError = false; + } + + /** + * Display a message and fade it out. + */ + onUploadSuccess() { + this.isUploading = false; + this.canvasContext.putImageData(this.canvasImageData, 0, 0); + this.canvasContext.fillText('✓', 30, 80); + this.canvasContext.stroke(); + setTimeout(() => { + this.canvasContext.putImageData(this.canvasImageData, 0, 0); + }, 750); + } + + /** + * Handle upload errors + */ + onUploadFailure() { + this.isUploading = false; + this.hasError = true; + } + + /** + * Updade upload progress in the UI. + * + * @param {number} percentage Upload progress percentage. + * @param {string} color Color to use. + */ + setUploadProgress(percentage, color) { + if (!this.hasError) { + this.drawProgress(percentage, color); + } + } + + /** + * SystemPatcher status event handler. + * Event payload can be: + * - "downloading " + * - "installing" + * - "ready opengapps" + * + * @param {string} message Event payload. + */ + onSystemPatcherStatusEvent(message) { + const textProgressPercent = document.getElementsByClassName('gm-upload-in-progress-txt-percent')[0]; + const textProgress = document.getElementsByClassName('gm-upload-in-progress-txt-progress')[0]; + const progressPercent = document.getElementsByClassName('gm-upload-in-progress-percent')[0]; + const data = message.split(' '); + + if (data[0] === 'downloading' && data.length >= 3 && textProgressPercent && + textProgress && progressPercent) { + const bytesDone = parseInt(data[1]); + const totalBytes = parseInt(data[2]); + if (totalBytes === 0 || Number.isNaN(totalBytes)) { + return; + } + + const percent = Math.round(100 * bytesDone / totalBytes); + textProgressPercent.innerHTML = percent + '%'; + progressPercent.style.width = percent + '%'; + + // Convert in Mb + const Mb = 1024 * 1024; + const mbDone = Math.round(10 * bytesDone / Mb) / 10; + const totalMb = Math.round(10 * totalBytes / Mb) / 10; + const msg = 'Downloading '+ mbDone +' Mb/' + totalMb +' Mb'; + textProgress.innerHTML = msg; + } else if (data[0] === 'installing' && textProgress) { + textProgress.innerHTML = this.i18n.UPLOADER_INSTALLING || 'Installing...'; + } else if (data[0] === 'ready' && data.length >= 2) { + if (data[1].indexOf('opengapps') !== -1) { + this.updateOpenGAppsStatus(true); + } + + if (!this.flashing) { + return; + } + + const json = { + channel: 'systempatcher', messages: [ + 'notify last_result' + ] + }; + this.instance.sendEvent(json); + } + } + + /** + * SystemPatcher lastresult event handler. + * + * @param {string} message Event payload. + */ + onSystemPatcherLastResultEvent(message) { + switch (message) { + case 'success': + this.displayStep('successScreen'); + // On flashing success, force display success screen + if (this.flashing && this.widget.classList.contains('gm-hidden')) { + this.toggleWidget(); + } + this.flashing = false; + break; + case 'unavailable': + case 'network_error': + case 'corrupted_archive': + case 'install_error': + this.displayStep('errorScreen'); + // On flashing error, force display errorscreen + if (this.flashing && this.widget.classList.contains('gm-hidden')) { + this.toggleWidget(); + } + this.flashing = false; + break; + default: + break; + } + } + + /** + * Create a canvas to provide an area to display upload progress. + */ + createUploadProgressCanvas() { + if (this.canvasContext) { + return; + } + + const canvas = document.createElement('canvas'); + canvas.classList.add('gm-upload-progress'); + canvas.width = 120; + canvas.height = 120; + this.instance.wrapper.insertBefore(canvas, this.instance.wrapper.firstChild); + this.canvasContext = canvas.getContext('2d'); + this.canvasImageData = null; + + this.canvasContext.beginPath(); + this.canvasContext.strokeStyle = '#E6195E'; + this.canvasContext.fillStyle = '#E6195E'; + this.canvasContext.lineCap = 'square'; + this.canvasContext.font = 'bold 60px Arial'; + this.canvasContext.lineWidth = 10.0; + this.canvasContext.closePath(); + this.canvasContext.fill(); + + this.canvasImageData = this.canvasContext.getImageData(0, 0, 120, 120); + } + + /** + * Draw upload progress (0 = nothing displayed; 1 = full circle). + * + * @param {number} percentage Upload progress percentage. + * @param {string} color Color to use. + */ + drawProgress(percentage, color) { + color = typeof color !== 'undefined' ? color : '#E6195E'; + this.canvasContext.strokeStyle = color; + this.canvasContext.fillStyle = color; + this.canvasContext.putImageData(this.canvasImageData, 0, 0); + this.canvasContext.beginPath(); + this.canvasContext.arc(60, 60, 55, -QUART, CIRC * percentage - QUART, false); + percentage = Math.round(percentage * 100); + const text = percentage !== 100 ? percentage : '99'; // do not display 100 (3 digit) + this.canvasContext.fillText(text, 25, 80); + this.canvasContext.stroke(); + } + + /** + * Create the all file upload screens. + */ + createStepper() { + this.stepper.homeScreen = this.createHomeScreen.bind(this); + this.stepper.disclaimerScreen = this.createDisclaimerScreen.bind(this); + this.stepper.uploadScreen = this.createUploadScreen.bind(this); + this.stepper.successScreen = this.createSuccessStep.bind(this); + this.stepper.errorScreen = this.createErrorStep.bind(this); + } + + /** + * Display a specific file upload screen. + * + * @param {string} step Screen name. + */ + displayStep(step) { + this.currentStep = step; + // Reset main content + this.inputs.innerHTML = ''; + + // Set current step + const template = this.stepper[step](); + this.title.innerHTML = template.title; + this.inputs.appendChild(template.body); + } + + /** + * Enable or disable the file upload capability. + * + * @param {boolean} available Upload availability. + */ + setAvailability(available) { + this.capabilityAvailable = available; + } + + /** + * Create and return the "home" file upload screen. + * + * @return {Object} HTMLElement & title for this screen. + */ + createHomeScreen() { + // Main div on the home page of the Open GApps widget + const homeContent = document.createElement('div'); + const headerHomeContent = document.createElement('div'); + headerHomeContent.className = 'gm-upload-main'; + + // Define image on home view of Open GApps popup + headerHomeContent.appendChild(this.createIconDiv('gm-upload-main-img')); + + // Define small text zone on home view + headerHomeContent.appendChild(this.createTextDiv( + 'gm-upload-main-txt', + '

You can install Open GApps, or upload a file: APK files will be installed, ' + + 'flashable archives will be flashed, and other files types will be pushed ' + + 'to the /sdcard/download folder on the device.

' + )); + + // Slit in header and bottom part + homeContent.appendChild(headerHomeContent); + + this.uploader = document.createElement('input'); + this.uploader.className = 'gm-uploader-file'; + this.uploader.type = 'file'; + this.uploader.setAttribute('gm-hidden', ''); + homeContent.appendChild(this.uploader); + + this.uploader.onchange = () => { + if (!this.isUploading) { + this.toggleWidget(); + this.upload(this.uploader.files); + } + }; + + // Define 2 buttons at widget bottom + const bottomButtonsContainer = document.createElement('div'); + bottomButtonsContainer.className = 'gm-upload-main-bottom-buttons'; + + const buttons = this.createButtonsGroup('Install Open GApps', (event) => { + event.preventDefault(); + + if (this.opengappsInstalled === false && this.capabilityAvailable) { + this.displayStep('disclaimerScreen'); + } + }, 'Browse', (event) => { + event.preventDefault(); + this.uploader.click(); + }); + + this.installButton = buttons[LEFT]; + this.updateOpenGAppsStatus(this.opengappsInstalled); + + bottomButtonsContainer.appendChild(buttons[LEFT]); + bottomButtonsContainer.appendChild(buttons[RIGHT]); + + // Add buttons and all home content view for this widget in the DOM + homeContent.appendChild(bottomButtonsContainer); + + return { + body: homeContent, + title: this.i18n.UPLOADER_HOME_TITLE || 'Upload file to the virtual device', + }; + } + + /** + * Update the "home" file upload screen. + * + * @param {boolean} installed Whether or not OpenGApps are installed on the instance. + */ + updateOpenGAppsStatus(installed) { + this.opengappsInstalled = installed; + + if (!this.installButton) { + return; + } + + if (!this.capabilityAvailable) { + this.installButton.className = + 'gm-upload-main-bottom-buttons-left gm-dont-close gm-uploader-install-opengapps ' + + 'gm-upload-main-bottom-buttons-disabled'; + this.installButton.innerHTML = 'Install Open GApps'; + this.installButton.title = 'Open GApps are not available for this virtual device'; + return; + } + + if (installed) { + this.installButton.className = + 'gm-upload-main-bottom-buttons-left gm-dont-close gm-uploader-install-opengapps ' + + 'gm-upload-main-bottom-buttons-disabled'; + this.installButton.innerHTML = 'Open GApps installed'; + this.installButton.title = 'Open GApps are already installed'; + } else { + this.installButton.className = + 'gm-upload-main-bottom-buttons-left gm-dont-close gm-uploader-install-opengapps'; + + this.installButton.innerHTML = 'Install Open GApps'; + this.installButton.title = 'Install Open GApps'; + } + } + + /** + * Create and return the "disclaimer" file upload screen. + * + * @return {Object} HTMLElement & title for this screen. + */ + createDisclaimerScreen() { + // Main div on the home page of the Opengapps widget + const homeContent = document.createElement('div'); + const headerHomeContent = document.createElement('div'); + headerHomeContent.className = 'gm-upload-disclaimer'; + + // Define small text zone on home view + headerHomeContent.appendChild(this.createTextDiv('gm-upload-disclaimer-txt', '

PLEASE NOTE
\ + GENYMOBILE INC., ASSUMES NO LIABILITY WHATSOEVER RESULTING FROM THE DOWNLOAD,\ + INSTALL AND USE OF GOOGLE PLAY SERVICES WITHIN YOUR VIRTUAL DEVICES. YOU ARE\ + SOLELY RESPONSIBLE FOR THE USE AND ASSUME ALL LIABILITY RELATED THERETO.\ + MOREOVER\ +

\ + GENYMOBILE INC. DISCLAIMS ANY WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,\ + INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF MERCHANTABILITY, OR FITNESS\ + FOR A PARTICULAR PURPOSE REGARDING THE COMPATIBILITY OF THE OPENGAPPS PACKAGES\ + WITH ANY VERSION OF GENYMOTION. IN NO EVENT SHALL GENYMOBILE INC. OR ITS AFFILIATES,\ + OR THEIR RESPECTIVE OFFICERS, DIRECTORS, EMPLOYEES, OR AGENTS BE LIABLE WITH RESPECT\ + TO YOUR DOWNLOAD OR USE OF THE GOOGLE PLAY SERVICES AND YOU RELEASE GENYMOBILE INC.\ + FROM ANY LIABILITY RELATED THERETO. YOU AGREE TO DEFEND, INDEMNIFY AND HOLD HARMLESS\ + GENYMOBILE INC. FOR ANY CLAIMS OR COSTS RELATED TO YOUR USE OR DOWNLOAD OF THE GOOGLE\ + PLAY SERVICES.\ +

')); + + // Slit in header and bottom part + homeContent.appendChild(headerHomeContent); + + // Define 2 buttons at widget bottom + const bottomButtonsContainer = document.createElement('div'); + bottomButtonsContainer.className = 'gm-upload-main-bottom-buttons'; + + const buttons = this.createButtonsGroup('Back', (event) => { + event.preventDefault(); + this.flashing = false; + this.displayStep('homeScreen'); + }, 'Install', (event) => { + event.preventDefault(); + this.flashing = true; + const json = { + channel: 'systempatcher', messages: [ + 'install opengapps' + ] + }; + this.instance.sendEvent(json); + this.displayStep('uploadScreen'); + }); + + bottomButtonsContainer.appendChild(buttons[LEFT]); + bottomButtonsContainer.appendChild(buttons[RIGHT]); + + // Add buttons and all home content view for this widget in the DOM + homeContent.appendChild(bottomButtonsContainer); + + return { + body: homeContent, + title: this.i18n.UPLOADER_DISCLAIMER || 'You are about to install Open GApps on your virtual device', + }; + } + + /** + * Create and return the "progress" file upload screen. + * + * @return {Object} HTMLElement & title for this screen. + */ + createUploadScreen() { + // Main div on the upload page of the Opengapps widget + const uploadContent = document.createElement('div'); + const headerUploadContent = document.createElement('div'); + headerUploadContent.className = 'gm-upload-in-progress'; + + // Define small text zone on upload view + const txtProgress = this.createTextDiv('gm-upload-in-progress-txt-progress', ''); + const txtPercent = this.createTextDiv('gm-upload-in-progress-txt-percent', ''); + const bgPercentBar = this.createTextDiv('gm-upload-in-progress-bg-percent', ''); + const percentbar = this.createTextDiv('gm-upload-in-progress-percent', ''); + + percentbar.style.width = '0%'; + bgPercentBar.appendChild(percentbar); + + headerUploadContent.appendChild(txtProgress); + headerUploadContent.appendChild(txtPercent); + headerUploadContent.appendChild(bgPercentBar); + + // Slit in header and bottom part + uploadContent.appendChild(headerUploadContent); + + // Define 2 buttons at widget bottom + const bottomButtonsContainer = document.createElement('div'); + bottomButtonsContainer.className = 'gm-upload-main-bottom-buttons'; + + const buttons = this.createButtonsGroup(null, null, 'Cancel', (event) => { + event.preventDefault(); + const json = { + channel: 'systempatcher', messages: [ + 'cancel' + ] + }; + this.instance.sendEvent(json); + this.displayStep('disclaimerScreen'); + }); + + bottomButtonsContainer.appendChild(buttons[RIGHT]); + + // Add buttons and all upload content view for this widget in the DOM + uploadContent.appendChild(bottomButtonsContainer); + + return { + body: uploadContent, + title: this.i18n.UPLOADER_INPROGRESS || 'Downloading', + }; + } + + /** + * Create and return the "success" file upload screen. + * + * @return {Object} HTMLElement & title for this screen. + */ + createSuccessStep() { + // Main div on the home page of the Open GApps widget + const homeContent = document.createElement('div'); + const headerHomeContent = document.createElement('div'); + headerHomeContent.className = 'gm-upload-success'; + + // Define image on home view of Open GApps popup + headerHomeContent.appendChild(this.createIconDiv('gm-upload-success-img')); + + // Small text below the image + headerHomeContent.appendChild(this.createTextDiv('gm-upload-success-caption', 'Congratulation!')); + + // Define small text zone on home view + headerHomeContent.appendChild(this.createTextDiv( + 'gm-upload-success-txt', '

Open GApps have been successfully \ + installed on the virtual device.
Please restart the virtual device to complete the installation.
\ + You will be redirected to the Open GApps website.

' + )); + + // Reboot button + headerHomeContent.appendChild(this.createButton('gm-upload-success-button', 'Reboot device', (event) => { + event.preventDefault(); + const json = { + channel: 'systempatcher', messages: [ + 'reboot' + ] + }; + this.instance.sendEvent(json); + this.toggleWidget(); + })); + + // Slit in header and bottom part + homeContent.appendChild(headerHomeContent); + + return { + body: homeContent, + title: this.i18n.UPLOADER_SUCCESS || 'Open GApps successfully installed', + }; + } + + /** + * Create and return the "error" file upload screen. + * + * @return {Object} HTMLElement & title for this screen. + */ + createErrorStep() { + // Main div on the home page of the Open GApps widget + const homeContent = document.createElement('div'); + const headerHomeContent = document.createElement('div'); + headerHomeContent.className = 'gm-upload-error'; + + // Define image on home view of Open GApps popup + headerHomeContent.appendChild(this.createIconDiv('gm-upload-error-img')); + + // Small text below the image + headerHomeContent.appendChild(this.createTextDiv('gm-upload-error-caption', 'Failed to install Open GApps')); + + // Slit in header and bottom part + homeContent.appendChild(headerHomeContent); + + return { + body: homeContent, + title: this.i18n.UPLOADER_FAILURE || 'We\'ve got a problem…', + }; + } + + /** + * Create and return a buttons group. + * + * @param {string} leftText Left button text. + * @param {Function} leftCallback Left button click callback. + * @param {string} rightText Right button text. + * @param {Function} rightCallback Right button click callback. + * @return {HTMLElement} The buttons group. + */ + createButtonsGroup(leftText, leftCallback, rightText, rightCallback) { + const buttons = [null, null]; + + if (leftText) { + buttons[LEFT] = this.createButton( + 'gm-upload-main-bottom-buttons-left gm-dont-close', leftText, leftCallback + ); + } + + if (rightText) { + buttons[RIGHT] = this.createButton( + 'gm-upload-main-bottom-buttons-right gm-dont-close', rightText, rightCallback + ); + } + + return buttons; + } + + /** + * Create and return a button. + * + * @param {string} className CSS class to apply to the element. + * @param {string} text Button text content. + * @param {Function} onClick Click callback function. + * @return {HTMLElement} The created button. + */ + createButton(className, text, onClick) { + const button = document.createElement('div'); + button.className = className; + button.innerHTML = text; + button.onclick = onClick; + return button; + } + + /** + * Create and return an icon element. + * + * @param {string} className CSS class to apply to the element. + * @return {HTMLElement} The created icon. + */ + createIconDiv(className) { + const icon = document.createElement('span'); + icon.className = 'gm-upload-icon ' + className; + return icon; + } + + /** + * Create and return a text element. + * + * @param {string} className CSS class to apply to the element. + * @param {string} text Block text content. + * @return {HTMLElement} The created text. + */ + createTextDiv(className, text) { + const textCloud = document.createElement('div'); + textCloud.className = className; + textCloud.innerHTML = text; + return textCloud; + } +}; diff --git a/src/plugins/Fullscreen.js b/src/plugins/Fullscreen.js new file mode 100644 index 00000000..124798e9 --- /dev/null +++ b/src/plugins/Fullscreen.js @@ -0,0 +1,131 @@ +'use strict'; + +/** + * Display fullscreen plugin. + * Gives the ability to display the player on the whole screen. + */ +module.exports = class Fullscreen { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + */ + constructor(instance) { + // Reference instance + this.instance = instance; + + // we register for fullscreen event changes + if (document.addEventListener) { + document.addEventListener('webkitfullscreenchange', this.onFullscreenEvent.bind(this), false); + document.addEventListener('fullscreenchange', this.onFullscreenEvent.bind(this), false); + } + + // Display widget + this.renderToolbarButton(); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + + if (this.isTemplateFullscreen()) { + /** + * If the template is fullscreen, there is a splash screen that ask the user + * to allow the fullscreen. It is needed due to HTML5 requirements. + */ + const fullscreenMessage = this.instance.getChildByClass(this.instance.root, 'gm-fullscreen-message'); + fullscreenMessage.onclick = () => { + fullscreenMessage.classList.add('hide'); + this.instance.wrapper.classList.remove('hide'); + this.goFullscreen(this.instance.root); + }; + } else { + /** + * Else we add a button that will allows the user to set/unset the + * fullscreen for the given genycloud instance + */ + this.button = document.createElement('li'); + this.image = document.createElement('div'); + this.image.className = 'gm-icon-button gm-fullscreen-button'; + this.image.title = 'Fullscreen'; + this.button.appendChild(this.image); + toolbar.appendChild(this.button); + + // when clicked on the button, should we enter or exit fullscreen + this.button.onclick = () => { + if (this.fullscreenEnabled()) { + this.exitFullscreen(); + } else { + this.goFullscreen(this.instance.root); + } + }; + } + } + + /** + * Enter fullscreen mode. + * + * @param {HTMLElement} element DOM element to set fullscreen on. + */ + goFullscreen(element) { + this.instance.wrapper.classList.add('gm-fullscreen'); + this.image.classList.add('gm-active'); + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } + } + + /** + * Determine whether fullscreen mode is active or not. + * + * @return {boolean} Whether fullscreen mode is active or not. + */ + fullscreenEnabled() { + return document.fullscreenElement + || document.webkitFullscreenElement; + } + + /** + * Exit fullscreen mode. + */ + exitFullscreen() { + this.instance.wrapper.classList.remove('gm-fullscreen'); + this.image.classList.remove('gm-active'); + if (!this.fullscreenEnabled()) { + return; // do not try to remove fulllscreen if it is not active + } + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + } + + /** + * Determine whether the template is configured to be displayed in fullscreen or not. + * + * @return {boolean} Whether the template is configured to be displayed in fullscreen or not. + */ + isTemplateFullscreen() { + return Boolean(this.instance.getChildByClass(this.instance.root, 'gm-fullscreen-message')); + } + + /** + * Fullscreen event listener. + */ + onFullscreenEvent() { + // if we lose fullscreen, we have to make sure that it has correctly exited + if (!this.fullscreenEnabled()) { + this.exitFullscreen(); + } + } +}; diff --git a/src/plugins/GPS.js b/src/plugins/GPS.js new file mode 100644 index 00000000..c364e3a4 --- /dev/null +++ b/src/plugins/GPS.js @@ -0,0 +1,578 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +/* global google */ + +/** + * Instance GPS plugin. + * Provides GPS control. + */ +module.exports = class GPS extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + * @param {boolean} speedSupport Enable speed support. + */ + constructor(instance, i18n, speedSupport) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.gps = this; + + // Location fields + this.fields = ['altitude', 'longitude', 'latitude', 'accuracy', 'bearing']; + if (speedSupport) { + this.fields.push('speed'); + } + + // Map references + this.map = null; + if (typeof google !== 'undefined') { + this.elevationService = new google.maps.ElevationService(); + } else { + log.error('Cant find Google Maps API object, did you forget to embed it?'); + this.elevationService = false; + } + this.markers = []; + + // Let's use Dalvik as default value + this.mapLat = 65.9667; + this.mapLng = -18.5333; + this.elevation = 15.04444408; + + // The minimal zoom level of the map (if less, it will zoom automatically) + this.minimumZoomLevel = 8; + + // Display widget + this.renderToolbarButton(); + this.renderGPSForm(); + this.renderMapView(); + + // Listen for gps events: " " + this.instance.registerEventCallback('gps', (message) => { + const values = message.split(' '); + if (this.fields.includes(values[0]) && values.length >= 2) { + this.setFieldValue('gm-gps-' + values[0], values[1]); + } + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-gps-button'; + this.toolbarBtnImage.title = this.i18n.GPS_TITLE || 'GPS'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleForm.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget: map view. + */ + renderMapView() { + // Create elements + this.mapWidget = document.createElement('div'); + this.mapview = document.createElement('div'); + + // Add capture button + const capture = document.createElement('button'); + capture.innerHTML = this.i18n.GPS_CAPTURE || 'Capture'; + capture.className = 'gm-gps-mapview-capture'; + capture.onclick = this.onMapClicked.bind(this); + + // Add cancel button + const cancel = document.createElement('button'); + cancel.innerHTML = this.i18n.GPS_CANCEL || 'Cancel'; + cancel.className = 'gm-gps-mapview-cancel'; + cancel.onclick = this.onHideMapButtonClicked.bind(this); + + // Setup + this.mapview.className = 'gm-mapview'; + this.mapWidget.className = 'gm-overlay gm-gps-mapview gm-hidden'; + this.mapWidget.appendChild(this.mapview); + this.mapWidget.appendChild(capture); + this.mapWidget.appendChild(cancel); + + // Render into document + this.overlays.push(this.mapWidget); + this.instance.root.appendChild(this.mapWidget); + } + + /** + * Load map, if available. + */ + loadMap() { + // Get form info + const info = this.getLocationInfo(); + + // Render map + if (typeof google !== 'undefined') { + this.map = new google.maps.Map(this.mapview, { + center: { + lat: info.latitude, + lng: info.longitude, + }, + zoom: this.minimumZoomLevel, + }); + + // Add initial marker for selection from form + this.addMapMarker(info.latitude, info.longitude); + + // Listen for new location + this.map.addListener('click', (event) => { + this.clearMarkers(); + // Add new marker / capture coords for click location + this.addMapMarker(event.latLng.lat(), event.latLng.lng()); + }); + } + } + + /** + * Display or hide the map view. + */ + toggleMapview() { + this.mapWidget.classList.toggle('gm-hidden'); + } + + /** + * Create a form field element. + * + * @param {string} name Input name. + * @param {string} label Input label. + * @param {string} value Input value. + * @param {string} min Input min attribute. + * @param {string} max Input max attribute. + * @return {HTMLElement} The created input. + */ + generateInput(name, label, value, min, max) { + const inputWrap = document.createElement('div'); + + const lab = document.createElement('label'); + lab.innerHTML = label; + inputWrap.appendChild(lab); + + const input = document.createElement('input'); + input.type = 'number'; + input.defaultValue = value; + input.min = min; + input.max = max; + input.className = 'gm-gps-' + name; + input.step = 'any'; + input.addEventListener('keyup', this.checkErrors.bind(this)); + inputWrap.appendChild(input); + + return inputWrap; + } + + /** + * Input form validation. + */ + checkErrors() { + let gotAnError = false; + this.fields.forEach((field) => { + const input = this.instance.getChildByClass(this.instance.root, 'gm-gps-' + field); + if (input.checkValidity() === false) { + input.classList.add('gm-error'); + gotAnError = true; + } else { + input.classList.remove('gm-error'); + } + }); + + this.instance.getChildByClass(this.instance.root, 'gm-gps-submit').disabled = gotAnError; + } + + /** + * Render the widget: controls view. + */ + renderGPSForm() { + // Create elements + this.formWidget = document.createElement('div'); + this.form = document.createElement('form'); + const formWrap = document.createElement('div'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.GPS_TITLE || 'GPS'; + this.form.appendChild(title); + + // Generate form inputs + const inputs = document.createElement('div'); + inputs.className = 'gm-col'; + + const latitudeLabel = this.i18n.GPS_LATITUDE || 'Latitude (°)'; + inputs.appendChild(this.generateInput( + 'latitude', latitudeLabel, this.mapLat, -90.0, 90.0 + )); + + const longitudeLabel = this.i18n.GPS_LONGITUDE || 'Longitude (°)'; + inputs.appendChild(this.generateInput( + 'longitude', longitudeLabel, this.mapLng, -180.0, 180.0 + )); + + const altitudeLabel = this.i18n.GPS_ALTITUDE || 'Altitude (m)'; + inputs.appendChild(this.generateInput( + 'altitude', altitudeLabel, this.elevation, -10000, 10000 + )); + + const accuracyLabel = this.i18n.GPS_ACCURACY || 'Accuracy (m)'; + inputs.appendChild(this.generateInput('accuracy', accuracyLabel, 0, 0, 200)); + + const bearingLabel = this.i18n.GPS_BEARING || 'Bearing (°)'; + inputs.appendChild(this.generateInput('bearing', bearingLabel, 0, 0, 360)); + + if (this.fields.includes('speed')) { + const speedLabel = this.i18n.GPS_SPEED || 'Speed (m/s)'; + inputs.appendChild(this.generateInput('speed', speedLabel, 0, 0, 399.99)); + } + + // Generate right side of form + const right = document.createElement('div'); + const rightMapWrap = document.createElement('div'); + const map = document.createElement('button'); + this.rightGeolocWrap = document.createElement('div'); + this.geolocBtn = document.createElement('button'); + + right.className = 'gm-col'; + rightMapWrap.className = 'map-wrap'; + + map.className = 'map'; + map.innerHTML = this.i18n.GPS_MAP || 'MAP'; + map.onclick = this.onOpenMapButtonClicked.bind(this); + + this.rightGeolocWrap.className = 'gm-geoloc-wrap'; + this.geolocBtn.className = 'gm-gps-geoloc'; + this.geolocBtn.innerHTML = this.i18n.GPS_GEOLOC || 'My position'; + this.geolocBtn.onclick = this.onGeolocButtonClicked.bind(this); + + rightMapWrap.appendChild(map); + this.rightGeolocWrap.appendChild(this.geolocBtn); + right.appendChild(rightMapWrap); + right.appendChild(this.rightGeolocWrap); + + // Build form + formWrap.className = 'gm-wrap'; + formWrap.appendChild(inputs); + formWrap.appendChild(right); + this.form.appendChild(formWrap); + + // Attach submit button + const button = document.createElement('button'); + button.innerHTML = this.i18n.GPS_SUBMIT || 'Submit'; + button.className = 'gm-gps-submit'; + button.onclick = this.sendDataToInstance.bind(this); + this.form.appendChild(button); + + // Setup + this.formWidget.className = 'gm-overlay gm-gps-controls gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleForm.bind(this); + + this.formWidget.appendChild(close); + this.formWidget.appendChild(this.form); + + // Render into document + this.overlays.push(this.formWidget); + this.instance.root.appendChild(this.formWidget); + } + + /** + * Display or hide the controls view. + */ + toggleForm() { + // Notify other callers + if (this.formWidget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + this.checkForGeolocation(); + + // Toggle display + this.formWidget.classList.toggle('gm-hidden'); + this.mapWidget.classList.add('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Set geolocation button availability. + * + * @param {boolean} enabled Geolocation availability. + */ + setGeolocButtonAvailability(enabled) { + this.geolocBtn.disabled = !enabled; + if (enabled) { + this.rightGeolocWrap.title = this.i18n.GPS_GEOLOC_TOOLTIP || 'Get my position from browser location'; + } else { + this.rightGeolocWrap.title = this.i18n.GPS_NOGEOLOC_TOOLTIP || 'Geolocation not supported'; + } + } + + /** + * Open map view clicked. + * + * @param {Event} event Event. + */ + onOpenMapButtonClicked(event) { + event.preventDefault(); + this.toggleMapview(); + this.loadMap(); + } + + /** + * Geolocation button clicked. + * + * @param {Event} event Event. + */ + onGeolocButtonClicked(event) { + event.preventDefault(); + this.getLocation(); + } + + /** + * Map view clicked. + * + * @param {Event} event Event. + */ + onMapClicked(event) { + event.preventDefault(); + + // Update fields + this.setFieldValue('gm-gps-latitude', this.mapLat); + this.setFieldValue('gm-gps-longitude', this.mapLng); + this.setFieldValue('gm-gps-altitude', this.elevation); + + // Hide mapview + this.toggleMapview(); + } + + /** + * Hide map view button clicked. + * + * @param {Event} event Event. + */ + onHideMapButtonClicked(event) { + event.preventDefault(); + + this.mapWidget.classList.add('gm-hidden'); + } + + /** + * Send information to instance. + * + * @param {Event} event Event. + */ + sendDataToInstance(event) { + event.preventDefault(); + + const json = this.buildEventJson(); + if (json.messages.length) { + this.instance.sendEvent(json); + } + + this.toggleForm(); + } + + /** + * Get client geolocation. + */ + getLocation() { + if (!navigator.geolocation) { + return; + } + + navigator.geolocation.getCurrentPosition((position) => { + if (!position || !position.coords) { + return; + } + + this.fields.forEach((field) => { + if (position.coords[field]) { + this.setFieldValue('gm-gps-' + field, position.coords[field]); + } + }); + // Get altitude from elevation service if we don't have any + if (!position.coords.altitude && this.elevationService && + position.coords.latitude && position.coords.longitude) { + const location = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); + this.elevationService.getElevationForLocations({ + 'locations': [location], + }, (results, status) => { + if (status === 'OK') { + // Retrieve the first result + if (results[0]) { + this.setFieldValue('gm-gps-altitude', results[0].elevation); + } + } + }); + } + }); + } + + /** + * Check browser geolocation capability and update UI accordingly. + */ + checkForGeolocation() { + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition(() => { + this.setGeolocButtonAvailability(true); + }, () => { + this.setGeolocButtonAvailability(false); + }); + } else { + this.setGeolocButtonAvailability(false); + } + } + + /** + * Set the value of the given form input according. + * + * @param {HTMLElement} field Form input to update. + * @param {string} value Value to set. + */ + setFieldValue(field, value) { + value = Number(value); + if (Number.isNaN(value)) { + return; + } + + const inputField = this.instance.getChildByClass(this.instance.root, field); + if (!inputField) { + return; + } + + if (inputField.max) { + value = Math.min(value, inputField.max); + } + if (inputField.min) { + value = Math.max(value, inputField.min); + } + + inputField.value = value; + } + + /** + * Extract location info from inputs. + * + * @return {Object} Geolocation data. + */ + getLocationInfo() { + const info = {}; + this.fields.forEach((fieldName) => { + const field = this.instance.getChildByClass(this.instance.root, 'gm-gps-' + fieldName); + if (!field) { + log.debug(fieldName + ' field not found.'); + return; + } + let value = Number(field.value); + if (Number.isNaN(value)) { + return; + } + + if (field.max) { + value = Math.min(value, field.max); + } + if (field.min) { + value = Math.max(value, field.min); + } + + info[fieldName] = value; + }); + return info; + } + + /** + * Format GPS event to be send to the instance. + * + * @return {Object} GPS event. + */ + buildEventJson() { + const event = {channel: 'gps', messages: []}; + const info = this.getLocationInfo(); + this.fields.forEach((field) => { + event.messages.push('set ' + field + ' ' + info[field]); + }); + + if (event.messages.length) { + // make sure GPS is started + event.messages.push('enable'); + } + return event; + } + + /** + * Adds new marker at given coords. + * + * @param {number} lat Latitude of the marker. + * @param {number} lng Longitude of the marker. + */ + addMapMarker(lat, lng) { + this.mapLat = lat; + this.mapLng = lng; + + const marker = new google.maps.Marker({ + position: { + lat: this.mapLat, + lng: this.mapLng, + }, + map: this.map, + }); + this.markers.push(marker); + + // Center map and zoom on position when needed + if (this.map && this.map.getZoom() < this.minimumZoomLevel) { + this.map.setCenter(marker.getPosition()); + this.map.setZoom(this.minimumZoomLevel); + } + + if (this.elevationService) { + const location = new google.maps.LatLng(lat, lng); + this.elevationService.getElevationForLocations({ + 'locations': [location], + }, (results, status) => { + if (status === 'OK') { + // Retrieve the first result + if (results[0]) { + this.elevation = results[0].elevation; + } + } + }); + } + } + + /** + * Removes all existing markers from the map. + */ + clearMarkers() { + this.markers.forEach((marker) => { + marker.setMap(null); + }); + this.markers = []; + } +}; diff --git a/src/plugins/IOThrottling.js b/src/plugins/IOThrottling.js new file mode 100644 index 00000000..28a02b88 --- /dev/null +++ b/src/plugins/IOThrottling.js @@ -0,0 +1,304 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const PROFILES = require('./util/iothrottling-profiles'); +const BYTES_PER_KILOBYTE = 1024; +const BYTES_PER_MEGABYTE = BYTES_PER_KILOBYTE << 10; + +/** + * Instance IO throttling plugin. + * Provides disk I/O control. + */ +module.exports = class IOThrottling extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.diskIO = this; + + this.fields = {}; + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for initial diskio + this.instance.registerEventCallback('BLK', this.setActive.bind(this)); + + // Listen for diskio messages: "readbyterate " (or "cachecleared") + this.instance.registerEventCallback('diskio', (message) => { + const values = message.split(' '); + if (values[0] === 'readbyterate' && values.length === 2) { + this.updateDiskIOValues(values[1] / BYTES_PER_MEGABYTE); + } + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-iothrottling-button'; + this.toolbarBtnImage.title = this.i18n.IOTHROTTLING_TITLE || 'Disk I/O'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.form = document.createElement('form'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.IOTHROTTLING_TITLE || 'Disk I/O'; + this.form.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + + const IOThrottlingLabel = this.i18n.IOTHROTTLING_PROFILE || 'Profile'; + inputs.innerHTML = ''; + + // Create select + this.select = document.createElement('select'); + this.select.className = 'gm-iothrottling-select'; + const defaultOption = new Option(this.i18n.IOTHROTTLING_PROFILE_NONE || 'None'); + this.select.add(defaultOption); + this.select.onchange = this.changeProfile.bind(this); + inputs.appendChild(this.select); + + // Add option for each child + PROFILES.forEach((profile) => { + const option = new Option(profile.label, profile.name); + this.select.add(option); + }); + + this.readByteRateDiv = document.createElement('div'); + this.readByteRateDiv.classList.add('gm-fields'); + const readByteRateLabel = document.createElement('label'); + readByteRateLabel.innerHTML = this.i18n.IOTHROTTLING_READ_BYTERATE || 'Read speed limit:'; + this.readByteRate = document.createElement('input'); + this.readByteRate.className = 'gm-iothrottling-readbyterate'; + this.readByteRate.placeholder = this.i18n.IOTHROTTLING_READ_BYTERATE_EXAMPLE || 'eg: 100'; + this.readByteRate.title = this.i18n.READ_BYTE_RATE || 'Read speed limit'; + this.readByteRate.required = true; + this.readByteRate.pattern = '[0-9]*'; + this.readByteRateDiv.appendChild(readByteRateLabel); + const readByteRateSpeed = document.createElement('label'); + readByteRateSpeed.innerHTML = this.i18n.IOTHROTTLING_BYTERATE_UNIT || 'MiB per sec'; + readByteRateSpeed.classList.add('gm-units'); + this.readByteRateDiv.appendChild(readByteRateSpeed); + this.readByteRateDiv.appendChild(this.readByteRate); + + // Add submit button + this.submitBtn = document.createElement('button'); + this.submitBtn.className = 'gm-iothrottling-update'; + this.submitBtn.innerHTML = this.i18n.IOTHROTTLING_UPDATE || 'Update'; + this.submitBtn.onclick = this.sendDataToInstance.bind(this); + + // Add clear cache button + this.clearCacheBtn = document.createElement('button'); + this.clearCacheBtn.className = 'gm-iothrottling-clearcache'; + this.clearCacheBtn.innerHTML = this.i18n.IOTHROTTLING_CLEAR_CACHE || 'Clear cache'; + this.clearCacheBtn.onclick = this.clearCache.bind(this); + const clearCacheDiv = document.createElement('div'); + clearCacheDiv.appendChild(this.clearCacheBtn); + + // Setup + this.form.appendChild(inputs); + this.form.appendChild(this.readByteRateDiv); + this.form.appendChild(clearCacheDiv); + this.form.appendChild(this.submitBtn); + + this.setFieldsReadOnly(true); + this.resetFields('0'); + this.displayFields(false); + + this.widget.className = 'gm-overlay gm-iothrottling-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.form); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Set custom fields read-only or not. + * + * @param {boolean} readOnly Desired read-only status. + */ + setFieldsReadOnly(readOnly) { + this.readByteRate.readOnly = readOnly; + } + + /** + * Reset custom fields value. + * + * @param {number} value Desired value. + */ + resetFields(value) { + value = typeof value !== 'undefined' ? value : ''; + this.readByteRate.value = value; + } + + /** + * Toggle custom fields input visibility. + * + * @param {boolean} display Whether or not inputs should be visible. + */ + displayFields(display) { + if (display) { + this.readByteRateDiv.classList.remove('gm-hidden'); + } else { + this.readByteRateDiv.classList.add('gm-hidden'); + } + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Send information to instance. + * + * @param {Event} event Event. + */ + sendDataToInstance(event) { + event.preventDefault(); + + if (this.form.checkValidity()) { + const json = { + channel: 'diskio', messages: [ + 'set readbyterate ' + this.readByteRate.value * BYTES_PER_MEGABYTE, + 'clearcache' + ] + }; + this.instance.sendEvent(json); + this.toggleWidget(); + } + } + + /** + * Clear cache button handler. + * + * @param {Event} event Event. + */ + clearCache(event) { + event.preventDefault(); + + const json = {channel: 'diskio', messages: ['clearcache']}; + this.instance.sendEvent(json); + } + + /** + * Handles profile change. + */ + changeProfile() { + const profile = PROFILES.find((elem) => elem.name === this.select.value); + if (profile && profile.name !== 'Custom') { + this.loadDetails(profile); + this.displayFields(true); + } else if (profile && profile.name === 'Custom') { + this.setFieldsReadOnly(false); + this.displayFields(true); + } else { + this.resetFields('0'); + this.displayFields(false); + } + } + + /** + * Handles disk I/O parameters changes. Keeps UI in sync with the instance state. + * + * @param {number} readSpeed Read byte rate. + */ + updateDiskIOValues(readSpeed) { + readSpeed = Number(readSpeed); + + if (Number.isNaN(readSpeed) || readSpeed <= 0) { + this.select.value = this.i18n.IOTHROTTLING_PROFILE_NONE || 'None'; + this.resetFields('0'); + this.displayFields(false); + return; + } + + this.readByteRate.value = readSpeed; + const profile = PROFILES.find((elem) => elem.readByteRate === readSpeed); + if (profile && profile.name !== 'Custom') { + this.select.value = profile.name; + this.loadDetails(profile); + this.displayFields(true); + } else { + this.select.value = 'Custom'; + this.setFieldsReadOnly(false); + this.displayFields(true); + } + } + + /** + * Load fields with the given profile info. + * + * @param {Object} profile Selected profile. + */ + loadDetails(profile) { + if (profile.name !== 'Custom') { + this.setFieldsReadOnly(true); + this.readByteRate.value = profile.readByteRate; + } + } + + /** + * Activate disk I/O throttling. Keeps UI in sync with the instance state. + * + * @param {number} readSpeed Read byte rate. + */ + setActive(readSpeed) { + this.updateDiskIOValues(Number(readSpeed) / BYTES_PER_KILOBYTE); + } +}; diff --git a/src/plugins/Identifiers.js b/src/plugins/Identifiers.js new file mode 100644 index 00000000..cbafc6e2 --- /dev/null +++ b/src/plugins/Identifiers.js @@ -0,0 +1,310 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const HEX = '0123456789abcdef'; +const DIGITS = '0123456789'; + +/** + * Instance identifiers plugin. + * Provides device ID (IMEI) and Android ID control. + */ +module.exports = class Identifiers extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.identifiers = this; + + // Initial state + this.invalidAndroidId = false; + this.invalidDeviceId = false; + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for IDs from player + this.instance.registerEventCallback('ANDROID_ID', (payload) => { + this.androidInput.value = payload; + }); + this.instance.registerEventCallback('IMEI', (payload) => { + this.deviceInput.value = payload; + }); + + // Listen for settings messages: "parameter :" + this.instance.registerEventCallback('settings', (message) => { + const values = message.split(' '); + if (values[0] !== 'parameter' || values.length < 2) { + return; + } + + const deviceId = values[1].match(/(device_id:)(\w+)/); + if (deviceId) { + this.deviceInput.value = deviceId[2]; + } + const androidId = values[1].match(/(android_id:)(\w+)/); + if (androidId) { + this.androidInput.value = androidId[2]; + } + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-identifiers-button'; + this.toolbarBtnImage.title = this.i18n.IDENTIFIERS_TITLE || 'Identifiers'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.container = document.createElement('div'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.IDENTIFIERS_TITLE || 'Identifiers'; + this.container.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + + // Build field list + const android = this.generateInput( + 'android', 'Android ID', '', + this.generateRandomAndroidId.bind(this), + this.validateAndroidId.bind(this) + ); + const device = this.generateInput( + 'device', 'Device ID', '(IMEI/MEID)', + this.generateRandomDeviceId.bind(this), + this.validateDeviceId.bind(this) + ); + inputs.appendChild(android); + inputs.appendChild(device); + + this.submitBtn = document.createElement('button'); + this.submitBtn.innerHTML = this.i18n.IDENTIFIERS_UPDATE || 'Update'; + this.submitBtn.className = 'gm-action gm-identifiers-update'; + inputs.appendChild(this.submitBtn); + this.submitBtn.onclick = this.sendDataToInstance.bind(this); + + this.container.appendChild(inputs); + + // Setup + this.widget.className = 'gm-overlay gm-identifiers-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.container); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Send information to instance. + * + * @param {Event} event Event. + */ + sendDataToInstance(event) { + event.preventDefault(); + + const androidId = this.androidInput.value; + const deviceId = this.deviceInput.value; + + if (androidId && this.androidInput.checkValidity()) { + const json = { + channel : 'framework' , messages : [ + 'set parameter android_id:' + androidId + ] + }; + this.instance.sendEvent(json); + } + + if (deviceId && this.deviceInput.checkValidity()) { + const json = { + channel : 'settings' , messages : [ + 'set parameter device_id:' + deviceId + ] + }; + this.instance.sendEvent(json); + } + this.toggleWidget(); + } + + /** + * Generate a new random Android ID. + * + * @param {Event} event Event. + */ + generateRandomAndroidId(event) { + if (event) { + event.preventDefault(); + } + + this.androidInput.value = this.generateHash(16, HEX); + this.invalidAndroidId = false; + this.checkErrors(); + } + + /** + * Validate Android ID input value. + */ + validateAndroidId() { + this.invalidAndroidId = !this.androidInput.checkValidity(); + this.checkErrors(); + } + + /** + * Generate a new random Device ID. + * + * @param {Event} event Event. + */ + generateRandomDeviceId(event) { + if (event) { + event.preventDefault(); + } + + this.deviceInput.value = this.generateHash(15, DIGITS); + this.invalidDeviceId = false; + this.checkErrors(); + } + + /** + * Validate Device ID input value. + */ + validateDeviceId() { + this.invalidDeviceId = !this.deviceInput.checkValidity(); + this.checkErrors(); + } + + /** + * Input form validation. + */ + checkErrors() { + this.androidInput.classList[this.invalidAndroidId ? 'add' : 'remove']('gm-error'); + this.deviceInput.classList[this.invalidDeviceId ? 'add' : 'remove']('gm-error'); + + this.submitBtn.disabled = this.invalidAndroidId || this.invalidDeviceId; + } + + /** + * Create a form field element. + * + * @param {string} type Input type. + * @param {string} label Input label. + * @param {string} description Input description. + * @param {string} generationMethod Input random value generation method. + * @param {string} validationMethod Input value validation method. + * @return {HTMLElement} The created input. + */ + generateInput(type, label, description, generationMethod, validationMethod) { + const field = document.createElement('div'); + const inputWrap = document.createElement('div'); + const input = document.createElement('input'); + const button = document.createElement('button'); + + inputWrap.className = 'gm-input-group'; + input.className = 'gm-identifier-' + type + '-input'; + input.type = 'text'; + input.required = true; + input.addEventListener('keyup', validationMethod); + inputWrap.appendChild(input); + + // Some customization + if (type === 'device') { + input.maxLength = 15; + input.pattern = '['+HEX+']{14,15}'; + } else if (type === 'android') { + input.maxLength = 16; + input.pattern = '['+HEX+']{16}'; + } + + button.className = 'gm-identifier-' + type + '-generate'; + button.innerHTML = this.i18n.IDENTIFIERS_GENERATE || 'Generate'; + button.onclick = generationMethod; + inputWrap.appendChild(button); + + field.className = 'gm-identifier-' + type; + field.innerHTML = ''; + field.appendChild(inputWrap); + + this[type + 'Input'] = input; + this[type + 'Gen'] = button; + + return field; + } + + /** + * Generate random 16 chars hash. + * + * @param {number} length Hash length. + * @param {string} alphabet Characters to use in for hash. + * @return {string} The generated hash. + */ + generateHash(length, alphabet) { + let hash = ''; + + for (let i = 0; i < length; i++) { + hash += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); + } + + return hash; + } +}; diff --git a/src/plugins/KeyboardEvents.js b/src/plugins/KeyboardEvents.js new file mode 100644 index 00000000..60a4ec5b --- /dev/null +++ b/src/plugins/KeyboardEvents.js @@ -0,0 +1,228 @@ +'use strict'; + +/** + * List of invisible key that can be pressed. + * For values, see http://doc.qt.io/qt-4.8/qt.html & https://github.com/ccampbell/mousetrap/blob/master/mousetrap.js + */ +const INVISIBLE_KEYS = { + 8: '0x01000003', // backspace + 9: '0x01000001', // tab + 13: '0x01000005', // enter + 16: '0x01000020', // shift + 17: '0x01000021', // ctrl + 18: '0x01000023', // alt + 20: '0x01000024', // capslock + 27: '0x01000000', // escape + 32: '0x20', // space + 33: '0x01000016', // pageup + 34: '0x01000017', // pagedown + 35: '0x01000011', // end + 36: '0x01000010', // home + 37: '0x01000012', // left + 38: '0x01000013', // up + 39: '0x01000014', // right + 40: '0x01000015', // down + 45: '0x01000006', // ins + 46: '0x01000007', // del + /** + * Do not propagate Meta to Android. It disables copy/cut/paste special keys when pressed + * 91: '0x01000022', // meta + * 93: '0x01000022', // meta + * 224: '0x01000022', // meta + */ +}; + +const CTRL_SHORTCUT_KEYS = { + 67: '0x010000cf', // Copy + 86: '0x010000e2', // Paste + 88: '0x010000d0', // Cut +}; + +/** + * Instance keyboard plugin. + * Translate and forward keyboard events to instance. + */ +module.exports = class KeyboardEvents { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + */ + constructor(instance) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.keyboardEvents = this; + this.instance.keyboardEventsEnabled = true; + + this.transmitKeys = true; + this.isListenerAdded = false; + this.currentlyPressedKeys = new Map(); + + // Listen for keyboard disable + this.instance.registerEventCallback('keyboard-disable', () => { + this.transmitKeys = false; + }); + + // Listen for keyboard enable + this.instance.registerEventCallback('keyboard-enable', () => { + this.transmitKeys = true; + }); + + // This avoid having continuously pressed keys because of alt+tab or any other command that blur from tab + window.addEventListener('blur', () => { + this.currentlyPressedKeys.forEach((value) => { + const text = ''; + const json = { + type: 'KEYBOARD_RELEASE', + keychar: text, + keycode: value, + }; + this.instance.sendEvent(json); + }); + this.currentlyPressedKeys.clear(); + }); + } + + /** + * Called when the user press a writable key (A-Z + 0-9 + symbols), send event to instance. + * + * @param {Event} event Event. + */ + onKeyPress(event) { + if (!this.transmitKeys) { + return; + } + const key = event.charCode; + let text = event.key || String.fromCharCode(key); + if (text === 'Spacebar') { + text = ' '; + } + let json = { + type: 'KEYBOARD_PRESS', + keychar: text, + keycode: key, + }; + this.instance.sendEvent(json); + json = { + type: 'KEYBOARD_RELEASE', + keychar: text, + keycode: key, + }; + this.instance.sendEvent(json); + } + + /** + * Called when the user press a key. Handle special events like backspace. + * + * @param {Event} event Event. + * @return {boolean} Whether or not the event must continue propagation. + */ + onKeyDown(event) { + if (!this.transmitKeys) { + return true; + } + + let key; + /** + * Convert invisible key or shortcut keys when ctrl/meta are pressed + * to Qt keycode in Base-16. + * We only handle shortcut keys (c,x,v) up when ctrl or meta are pressed: + * for OSX, onKeyUp() is never fired when meta is pressed so we need to emulate it. + */ + if (CTRL_SHORTCUT_KEYS[event.keyCode] && event.ctrlKey) { + key = parseInt(CTRL_SHORTCUT_KEYS[event.keyCode], 16); + } else if (CTRL_SHORTCUT_KEYS[event.keyCode] && event.metaKey) { + key = parseInt(CTRL_SHORTCUT_KEYS[event.keyCode], 16); + let json = { + type: 'KEYBOARD_PRESS', + keychar: '', + keycode: key, + }; + this.instance.sendEvent(json); + json = { + type: 'KEYBOARD_RELEASE', + keychar: '', + keycode: key, + }; + this.instance.sendEvent(json); + // No need to propagate event or the character will also be send + event.preventDefault(); + event.stopPropagation(); + event.returnValue = false; + return false; + } else if (INVISIBLE_KEYS[event.keyCode]) { + key = parseInt(INVISIBLE_KEYS[event.keyCode], 16); + } else { + return true; + } + + const json = { + type: 'KEYBOARD_PRESS', + keychar: '', + keycode: key, + }; + this.instance.sendEvent(json); + this.currentlyPressedKeys.set(key, key); + event.preventDefault(); + event.stopPropagation(); + event.returnValue = false; + return false; + } + + /** + * Called when the user release a key. Handle special events like backspace + * + * @param {Event} event Event. + * @return {boolean} Whether or not the event must continue propagation. + */ + onKeyUp(event) { + if (!this.transmitKeys) { + return true; + } + + let key; + /** + * Convert invisible key or shortcut keys when ctrl/meta are pressed + * to Qt keycode in Base-16. + * We only handle shortcut keys (c,x,v) up when ctrl is pressed: + * for OSX, onKeyUp() is never fired when meta is pressed so no need + * to handle the case here since we emulate a down up in onKeyDown() in this case. + */ + if (CTRL_SHORTCUT_KEYS[event.keyCode] && event.ctrlKey) { + key = parseInt(CTRL_SHORTCUT_KEYS[event.keyCode], 16); + } else if (INVISIBLE_KEYS[event.keyCode]) { + key = parseInt(INVISIBLE_KEYS[event.keyCode], 16); + } else { + return true; + } + + const json = { + type: 'KEYBOARD_RELEASE', + keychar: '', + keycode: key, + }; + this.instance.sendEvent(json); + this.currentlyPressedKeys.delete(key); + event.preventDefault(); + event.stopPropagation(); + event.returnValue = false; + return false; + } + + /** + * Bind all event listener callback. + */ + addKeyboardCallbacks() { + this.instance.root.tabIndex = 0; + + if (!this.isListenerAdded) { + this.instance.root.addEventListener('keypress', this.onKeyPress.bind(this)); + this.instance.root.addEventListener('keydown', this.onKeyDown.bind(this)); + this.instance.root.addEventListener('keyup', this.onKeyUp.bind(this)); + this.instance.root.focus(); + this.isListenerAdded = true; + } + } +}; diff --git a/src/plugins/MouseEvents.js b/src/plugins/MouseEvents.js new file mode 100644 index 00000000..350cdd6e --- /dev/null +++ b/src/plugins/MouseEvents.js @@ -0,0 +1,163 @@ +'use strict'; + +/** + * Instance mouse plugin. + * Forward touch events to instance. + */ +module.exports = class MouseEvents { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + */ + constructor(instance) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.mouseEvents = this; + this.instance.mouseEventsEnabled = true; + + this.leftButtonPressed = false; + this.boundEventListener = this.releaseAtPreviousPositionEvent.bind(this); + } + + /** + * Mouse press event handler. + * + * @param {Event} event Event. + */ + onMousePressEvent(event) { + this.instance.video.muted = false; + if (event.button !== 0) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + this.leftButtonPressed = true; + this.instance.x = this.instance.coordinateUtils.getXCoordinate(event); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(event); + + let json = ''; + if (event.ctrlKey || event.metaKey) { + json = { + type: 'FAKE_MULTI_TOUCH_PRESS', + mode: event.shiftKey ? 2 : 1, x: this.instance.x, y: this.instance.y, + }; + } else { + json = {type: 'MOUSE_PRESS', x: this.instance.x, y: this.instance.y}; + } + this.instance.sendEvent(json); + + document.addEventListener('mouseup', this.boundEventListener, false); + } + + /** + * Mouse release event handler. + * + * @param {Event} event Event. + */ + onMouseReleaseEvent(event) { + if (event.button !== 0) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + this.leftButtonPressed = false; + this.instance.x = this.instance.coordinateUtils.getXCoordinate(event); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(event); + let json = ''; + if (event.ctrlKey || event.metaKey) { + json = { + type: 'FAKE_MULTI_TOUCH_RELEASE', + mode: event.shiftKey ? 2 : 1, x: this.instance.x, y: this.instance.y, + }; + } else { + json = {type: 'MOUSE_RELEASE', x: this.instance.x, y: this.instance.y}; + } + this.instance.sendEvent(json); + + document.removeEventListener('mouseup', this.boundEventListener, false); + } + + /** + * External mouse release event classback. + * Called when onmouseup event occurs outside of the player. + * + * @param {Event} event Event. + */ + releaseAtPreviousPositionEvent(event) { + if (event.button !== 0) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + this.leftButtonPressed = false; + const json = { + type: event.ctrlKey || event.metaKey ? 'FAKE_MULTI_TOUCH_RELEASE' : 'MOUSE_RELEASE', + x: this.instance.x, + y: this.instance.y, + }; + this.instance.sendEvent(json); + + document.removeEventListener('mouseup', this.boundEventListener, false); + } + + /** + * Mouse move event handler. + * + * @param {Event} event Event. + */ + onMouseMoveEvent(event) { + if (!this.leftButtonPressed) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + this.instance.x = this.instance.coordinateUtils.getXCoordinate(event); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(event); + const json = {type: 'MOUSE_MOVE', x: this.instance.x, y: this.instance.y}; + this.instance.sendEvent(json); + } + + /** + * Mouse move event handler. + * + * @param {Event} event Event. + */ + onMouseWheelEvent(event) { + event.preventDefault(); + event.stopPropagation(); + this.instance.x = this.instance.coordinateUtils.getXCoordinate(event); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(event); + const delta = event.wheelDelta; + const json = {type: 'WHEEL', x: this.instance.x, y: this.instance.y, delta: delta}; + this.instance.sendEvent(json); + } + + /** + * Method called when context menu try to open. + * + * @param {Event} event Event. + */ + cancelContextMenu(event) { + event.preventDefault(); + event.stopPropagation(); + event.returnValue = false; + } + + /** + * Bind all event handlers to the video wrapper. + */ + addMouseCallbacks() { + this.instance.videoWrapper.addEventListener('mousedown', this.onMousePressEvent.bind(this), false); + this.instance.videoWrapper.addEventListener('mouseup', this.onMouseReleaseEvent.bind(this), false); + this.instance.videoWrapper.addEventListener('mousemove', this.onMouseMoveEvent.bind(this), false); + this.instance.videoWrapper.addEventListener('mousewheel', this.onMouseWheelEvent.bind(this), false); + this.instance.videoWrapper.addEventListener('contextmenu', this.cancelContextMenu.bind(this), false); + } +}; diff --git a/src/plugins/MultiTouchEvents.js b/src/plugins/MultiTouchEvents.js new file mode 100644 index 00000000..a4605bdf --- /dev/null +++ b/src/plugins/MultiTouchEvents.js @@ -0,0 +1,93 @@ +'use strict'; + +/** + * Instance multi touch plugin. + * Forward multi touch events to instance. + */ +module.exports = class MultiTouchEvents { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + */ + constructor(instance) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.touchEvents = this; + this.instance.touchEventsEnabled = true; + } + + /** + * Called when the user starts touching the screen + * + * @param {Event} event Event. + */ + onScreenTouchStart(event) { + event.preventDefault(); + event.stopPropagation(); + + const json = {type: 'MULTI_TOUCH', nb: event.targetTouches.length, mode: 0, points: []}; + for (let i = 0; i < event.targetTouches.length; i++) { + const touch = event.targetTouches[i]; + + this.instance.x = this.instance.coordinateUtils.getXCoordinate(touch); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(touch); + + json.points.push({x: this.instance.x, y: this.instance.y}); + } + this.instance.sendEvent(json); + } + + /** + * Called when the user is moving on the screen. + * + * @param {Event} event Event. + */ + onScreenTouchMove(event) { + event.preventDefault(); + event.stopPropagation(); + + const json = {type: 'MULTI_TOUCH', nb: event.targetTouches.length, mode: 2, points: []}; + for (let i = 0; i < event.targetTouches.length; i++) { + const touch = event.targetTouches[i]; + + this.instance.x = this.instance.coordinateUtils.getXCoordinate(touch); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(touch); + + json.points.push({x: this.instance.x, y: this.instance.y}); + } + this.instance.sendEvent(json); + } + + /** + * Called when the user stops touching the screen. + * + * @param {Event} event Event. + */ + onScreenTouchEnd(event) { + event.preventDefault(); + event.stopPropagation(); + + const json = {type: 'MULTI_TOUCH', nb: event.targetTouches.length, mode: 1, points: []}; + for (let i = 0; i < event.targetTouches.length; i++) { + const touch = event.targetTouches[i]; + + this.instance.x = this.instance.coordinateUtils.getXCoordinate(touch); + this.instance.y = this.instance.coordinateUtils.getYCoordinate(touch); + + json.points.push({x: this.instance.x, y: this.instance.y}); + } + this.instance.sendEvent(json); + } + + /** + * Bind all event handlers to the video wrapper. + */ + addTouchCallbacks() { + this.instance.videoWrapper.addEventListener('touchstart', this.onScreenTouchStart.bind(this), false); + this.instance.videoWrapper.addEventListener('touchend', this.onScreenTouchEnd.bind(this), false); + this.instance.videoWrapper.addEventListener('touchmove', this.onScreenTouchMove.bind(this), false); + } +}; diff --git a/src/plugins/Network.js b/src/plugins/Network.js new file mode 100644 index 00000000..a1c761b9 --- /dev/null +++ b/src/plugins/Network.js @@ -0,0 +1,475 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const PROFILES = require('./util/network-profiles'); + +/** + * Instance network plugin. + * Provides network I/O control. + */ +module.exports = class Network extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + * @param {boolean} basebandEnabled Whether or not baseband control is enabled. + */ + constructor(instance, i18n, basebandEnabled) { + super(instance); + + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.network = this; + this.i18n = i18n || {}; + + this.fields = {}; + + this.basebandEnabled = basebandEnabled; + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + + // Listen for initial network + this.instance.registerEventCallback('NETWORK', this.setActive.bind(this)); + + /* + * Listen for network messages: + * state wifi up_rate:: + * down_rate:: + * up_delay:: + * down_delay: + * up_pkt_loss:: + * down_pkt_loss:: + * dns_delay:: + */ + this.instance.registerEventCallback('network_profile', (message) => { + const values = message.split(' '); + if (values.length < 9) { + return; + } + const upSpeed = values[2].split(':'); + const downSpeed = values[3].split(':'); + const upDelay = values[4].split(':'); + const downDelay = values[5].split(':'); + const upPacketLoss = values[6].split(':'); + const downPacketLoss = values[7].split(':'); + const dnsDelay = values[8].split(':'); + + const isThrottlingEnabled = upSpeed[1] === 'enabled' + && downSpeed[1] === 'enabled' + && upDelay[1] === 'enabled' + && downDelay[1] === 'enabled' + && upPacketLoss[1] === 'enabled' + && downPacketLoss[1] === 'enabled' + && dnsDelay[1] === 'enabled'; + + const profile = PROFILES.find((elem) => { + return elem.downSpeed.value === parseFloat(downSpeed[2]) && + elem.downDelay.value === parseFloat(downDelay[2]) && + elem.downPacketLoss.value === parseFloat(downPacketLoss[2]) && + elem.upSpeed.value === parseFloat(upSpeed[2]) && + elem.upDelay.value === parseFloat(upDelay[2]) && + elem.upPacketLoss.value === parseFloat(upPacketLoss[2]) && + elem.dnsDelay.value === parseFloat(dnsDelay[2]); + }); + + if (profile && isThrottlingEnabled) { + this.select.value = profile.name; + } else { + this.select.value = this.i18n.NETWORK_DELECT_PROFILE || 'Select a profile'; + } + this.changeProfile(); + }); + + // Listen for baseband messages: " " + this.instance.registerEventCallback('baseband', (message) => { + const values = message.split(' '); + if (values.length < 3) { + return; + } + + if (values[0] === 'network' && values[1] === 'operator') { + this.networkOperatorMMC.value = values[2]; + } + if (values[0] === 'network' && values[1] === 'operator_name') { + this.networkOperatorName.value = values.slice(2).join(' '); + } + if (values[0] === 'sim' && values[1] === 'operator') { + this.simOperatorMMC.value = values[2]; + } + if (values[0] === 'sim' && values[1] === 'operator_name') { + this.simOperatorName.value = values.slice(2).join(' '); + } + if (values[0] === 'sim' && values[1] === 'imsi_id') { + this.simMSIN.value = values[2]; + } + if (values[0] === 'sim' && values[1] === 'phone_number') { + this.simOperatorPhoneNumber.value = values[2]; + } + }); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-network-button'; + this.toolbarBtnImage.title = this.i18n.NETWORK_TITLE || 'Network & Baseband'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.form = document.createElement('form'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.NETWORK_TITLE || 'Network & Baseband'; + this.form.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + + // Create select + this.select = document.createElement('select'); + const defaultOption = new Option(this.i18n.NETWORK_DELECT_PROFILE || 'Select a profile'); + this.select.add(defaultOption); + this.select.onchange = this.changeProfile.bind(this); + inputs.appendChild(this.select); + + // Add option for each child + PROFILES.slice().reverse() + .forEach((profile) => { + const option = new Option(profile.label, profile.name); + this.select.add(option); + }); + + // Create detail section + this.profileDetails = document.createElement('div'); + this.profileDetails.className = 'gm-profile-details gm-hidden'; + + // Add detail fields + this.profileDetails.appendChild(this.createDetailsSection('Download speed', 'downSpeed')); + this.profileDetails.appendChild(this.createDetailsSection('Upload speed', 'upSpeed')); + this.profileDetails.appendChild(this.createDetailsSection('Download delay', 'downDelay')); + this.profileDetails.appendChild(this.createDetailsSection('Upload delay', 'upDelay')); + this.profileDetails.appendChild(this.createDetailsSection('Download packet loss', 'downPacketLoss')); + this.profileDetails.appendChild(this.createDetailsSection('Upload packet loss', 'upPacketLoss')); + this.profileDetails.appendChild(this.createDetailsSection('DNS Delay', 'dnsDelay')); + + if (this.basebandEnabled) { + this.networkOperator = document.createElement('div'); + + const networkOperatorLabelDiv = document.createElement('div'); + networkOperatorLabelDiv.className = 'gm-section'; + const networkOperatorLabel = document.createElement('label'); + networkOperatorLabel.innerHTML = this.i18n.NETWORK_OPERATOR || 'Network Operator'; + networkOperatorLabelDiv.appendChild(networkOperatorLabel); + + const networkOperatorMMCDiv = document.createElement('div'); + networkOperatorMMCDiv.className = 'gm-fields'; + const networkOperatorMMCLabel = document.createElement('label'); + networkOperatorMMCLabel.innerHTML = 'MCC/MNC'; + this.networkOperatorMMC = document.createElement('input'); + this.networkOperatorMMC.className = 'gm-network-network-mmc'; + this.networkOperatorMMC.placeholder = 'eg: 20814'; + this.networkOperatorMMC.pattern = '[0-9]{5,6}'; + this.networkOperatorMMC.title = 'Operator MCC/MNC'; + networkOperatorMMCDiv.appendChild(networkOperatorMMCLabel); + networkOperatorMMCDiv.appendChild(this.networkOperatorMMC); + + const networkOperatorNameDiv = document.createElement('div'); + networkOperatorNameDiv.className = 'gm-fields'; + const networkOperatorNameLabel = document.createElement('label'); + networkOperatorNameLabel.innerHTML = 'Name'; + this.networkOperatorName = document.createElement('input'); + this.networkOperatorName.className = 'gm-network-network-name'; + this.networkOperatorName.placeholder = 'eg: Verizon'; + this.networkOperatorName.title = 'Operator Name'; + networkOperatorNameDiv.appendChild(networkOperatorNameLabel); + networkOperatorNameDiv.appendChild(this.networkOperatorName); + + this.networkOperator.appendChild(networkOperatorLabelDiv); + this.networkOperator.appendChild(networkOperatorMMCDiv); + this.networkOperator.appendChild(networkOperatorNameDiv); + + this.simOperator = document.createElement('div'); + + const simperatorLabelDiv = document.createElement('div'); + simperatorLabelDiv.className = 'gm-section'; + const simperatorLabel = document.createElement('label'); + simperatorLabel.innerHTML = this.i18n.NETWORK_SIM_OPERATOR || 'SIM Operator'; + simperatorLabelDiv.appendChild(simperatorLabel); + + const simOperatorMMCDiv = document.createElement('div'); + simOperatorMMCDiv.className = 'gm-fields'; + const simOperatorMMCLabel = document.createElement('label'); + simOperatorMMCLabel.innerHTML = 'MCC/MNC'; + this.simOperatorMMC = document.createElement('input'); + this.simOperatorMMC.className = 'gm-network-sim-mmc'; + this.simOperatorMMC.placeholder = 'eg: 20814'; + this.simOperatorMMC.pattern = '[0-9]{5,6}'; + this.simOperatorMMC.title = 'SIM Operator MCC/MNC'; + this.simOperatorMMC.addEventListener('keyup', this.checkSimImsiErrors.bind(this)); + simOperatorMMCDiv.appendChild(simOperatorMMCLabel); + simOperatorMMCDiv.appendChild(this.simOperatorMMC); + + const simOperatorNameDiv = document.createElement('div'); + simOperatorNameDiv.className = 'gm-fields'; + const simOperatorNameLabel = document.createElement('label'); + simOperatorNameLabel.innerHTML = 'Name'; + this.simOperatorName = document.createElement('input'); + this.simOperatorName.className = 'gm-network-sim-name'; + this.simOperatorName.placeholder = 'eg: AT&T'; + this.simOperatorName.title = 'SIM Operator Name'; + simOperatorNameDiv.appendChild(simOperatorNameLabel); + simOperatorNameDiv.appendChild(this.simOperatorName); + + const simMSINDiv = document.createElement('div'); + simMSINDiv.className = 'gm-fields'; + const simMSINLabel = document.createElement('label'); + simMSINLabel.innerHTML = 'MSIN'; + this.simMSIN = document.createElement('input'); + this.simMSIN.className = 'gm-network-sim-msin'; + this.simMSIN.placeholder = 'eg: 2176510739'; + this.simMSIN.pattern = '[0-9]{9,10}'; + this.simMSIN.title = 'SIM MSIN'; + this.simMSIN.addEventListener('keyup', this.checkSimImsiErrors.bind(this)); + simMSINDiv.appendChild(simMSINLabel); + simMSINDiv.appendChild(this.simMSIN); + + const simOperatorPhoneDiv = document.createElement('div'); + simOperatorPhoneDiv.className = 'gm-fields'; + const simOperatorPhoneLabel = document.createElement('label'); + simOperatorPhoneLabel.innerHTML = 'Phone Number'; + this.simOperatorPhoneNumber = document.createElement('input'); + this.simOperatorPhoneNumber.className = 'gm-network-sim-phone'; + this.simOperatorPhoneNumber.placeholder = 'eg: 8004337300'; + this.simOperatorPhoneNumber.pattern = '[0-9]*'; + this.simOperatorPhoneNumber.title = 'Phone Number'; + simOperatorPhoneDiv.appendChild(simOperatorPhoneLabel); + simOperatorPhoneDiv.appendChild(this.simOperatorPhoneNumber); + + this.simOperator.appendChild(simperatorLabelDiv); + this.simOperator.appendChild(simOperatorMMCDiv); + this.simOperator.appendChild(simMSINDiv); + this.simOperator.appendChild(simOperatorNameDiv); + this.simOperator.appendChild(simOperatorPhoneDiv); + } + + // Add submit button + this.submitBtn = document.createElement('button'); + this.submitBtn.className = 'gm-network-update'; + this.submitBtn.innerHTML = this.i18n.NETWORK_UPDATE || 'Update'; + this.submitBtn.onclick = this.sendDataToInstance.bind(this); + + // Setup + this.form.appendChild(inputs); + this.form.appendChild(this.profileDetails); + if (this.basebandEnabled) { + this.form.appendChild(this.networkOperator); + this.form.appendChild(this.simOperator); + } + this.form.appendChild(this.submitBtn); + + this.widget.className = 'gm-overlay gm-network-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.form); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * MSIN verification according to MCC/MNC length: + * - MCC/MNC is 5 digits => MSIN should be 10 digits + * - MCC/MNC is 6 digits => MSIN should be 9 digits + * Only do the check if both are not empty: we might set one and not the other + * (We can't handle the case where one was set previously and the other is now + * beeing configured.) + */ + checkSimImsiErrors() { + this.simMSIN.setCustomValidity(''); + if (this.simOperatorMMC.value.length > 0 && this.simMSIN.value.length > 0) { + if (this.simOperatorMMC.value.length === 6 && this.simMSIN.value.length !== 9) { + this.simMSIN.setCustomValidity('Should be 9'); + } else if (this.simOperatorMMC.value.length === 5 && this.simMSIN.value.length !== 10) { + this.simMSIN.setCustomValidity('Should be 10'); + } + } + } + + /** + * Send information to instance. + * + * @param {Event} event Event. + */ + sendDataToInstance(event) { + event.preventDefault(); + + if (!this.form.checkValidity()) { + return; + } + + const profile = PROFILES.find((elem) => elem.name === this.select.value); + if (profile) { + const msgs = []; + if (profile.id === 0) { + msgs.push('disable wifi all'); + } else { + msgs.push('enable wifi all'); + msgs.push('set wifi up_rate ' + profile.upSpeed.value); + msgs.push('set wifi down_rate ' + profile.downSpeed.value); + msgs.push('set wifi up_delay ' + profile.upDelay.value); + msgs.push('set wifi down_delay ' + profile.downDelay.value); + msgs.push('set wifi up_pkt_loss ' + profile.upPacketLoss.value); + msgs.push('set wifi down_pkt_loss ' + profile.downPacketLoss.value); + msgs.push('set wifi dns_delay ' + profile.dnsDelay.value); + } + const json = {channel: 'network_profile', messages: msgs}; + this.instance.sendEvent(json); + } + + const msgs = []; + if (this.basebandEnabled) { + if (this.networkOperatorMMC.value) { + msgs.push('network operator ' + this.networkOperatorMMC.value); + } + if (this.networkOperatorName.value) { + msgs.push('network operator_name ' + this.networkOperatorName.value); + } + + if (this.simOperatorMMC.value) { + msgs.push('sim operator ' + this.simOperatorMMC.value); + } + + if (this.simOperatorName.value) { + msgs.push('sim operator_name ' + this.simOperatorName.value); + } + + if (this.simMSIN.value) { + msgs.push('sim imsi_id ' + this.simMSIN.value); + } + + if (this.simOperatorPhoneNumber.value) { + msgs.push('sim phone_number ' + this.simOperatorPhoneNumber.value); + } + + if (msgs.length > 0) { + const json = {channel: 'baseband', messages: msgs}; + this.instance.sendEvent(json); + } + } + + this.toggleWidget(); + } + + /** + * Update form according to the selected profile. + */ + changeProfile() { + const profile = PROFILES.find((elem) => elem.name === this.select.value); + if (profile) { + this.loadDetails(profile); + this.profileDetails.classList.remove('gm-hidden'); + } else { + this.profileDetails.classList.add('gm-hidden'); + } + } + + /** + * Creates and return the widget "details" section. + * + * @param {string} label Section label. + * @param {string} type Section type. + * @return {HTMLElement} Details section + */ + createDetailsSection(label, type) { + const section = document.createElement('section'); + this.fields[type] = document.createElement('span'); + section.innerHTML = label + ': '; + section.appendChild(this.fields[type]); + + return section; + } + + /** + * Update UI according to the given profile. + * + * @param {Object} profile Profile to load. + */ + loadDetails(profile) { + Object.entries(profile).forEach(([field, val]) => { + if (field === 'label' || field === 'name' || field === 'id') { + return; + } + this.fields[field].innerHTML = val.label; + }); + } + + /** + * Update UI according to the current active profile. + * + * @param {string} id Profile id. + */ + setActive(id) { + const profile = PROFILES.find((elem) => elem.id === Number(id)); + if (!profile || !String(id).length) { + return; + } + + const options = this.select.getElementsByTagName('option'); + for (let i = 0; i < options.length; i++) { + const option = options[i]; + if (option.value === profile.name) { + option.selected = 'selected'; + } + } + this.changeProfile(); + } +}; diff --git a/src/plugins/PeerConnectionStats.js b/src/plugins/PeerConnectionStats.js new file mode 100644 index 00000000..309d0b9d --- /dev/null +++ b/src/plugins/PeerConnectionStats.js @@ -0,0 +1,110 @@ +'use strict'; + +/** + * Instance peer connections plugin. + * Provides peer connections statistics. + */ +module.exports = class PeerConnectionStats { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {RTCPeerConnection} connection WebRTC connection. + * @param {number} interval Stats update interval in ms. + */ + constructor(instance, connection, interval) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.peerConnectionStats = this; + + this.connection = connection; + + setTimeout(() => { + this.connection.getStats(null).then((stats) => { + stats.forEach((entry) => { + if (entry.kind === 'video') { + if (entry.type === 'inbound-rtp') { + const codec = stats.get(entry.codecId); + if (codec) { + this.logAndBroadcast('codec', codec.mimeType); + } + } + if (entry.type === 'track') { + this.logAndBroadcast('width', entry.frameWidth); + this.logAndBroadcast('height', entry.frameHeight); + this.logAndBroadcast('jitter', Math.round( + entry.jitterBufferDelay / entry.jitterBufferEmittedCount * 1000 + )); + } + } + if (entry.type === 'candidate-pair' && entry.state === 'succeeded') { + this.logAndBroadcast('rtt', entry.currentRoundTripTime * 1000); + const candidate = stats.get(entry.localCandidateId); + this.hideDefaultTurnWarning(); + if (candidate && candidate.candidateType === 'relay') { + this.logAndBroadcast('turn', true); + if (this.instance.options.turn.default) { + this.logAndBroadcast('default', true); + this.displayDefaultTurnWarning(); + } else { + this.logAndBroadcast('default', false); + } + } else { + this.logAndBroadcast('turn', false); + } + } + }); + }); + }, interval); + } + + /** + * Format & send a stats event. + * + * @param {string} type Event name. + * @param {string} value Stat value. + */ + logAndBroadcast(type, value) { + this.instance.dispatchEvent(type, {msg: value}); + } + + /** + * Creates & display the default turn warning. + */ + displayDefaultTurnWarning() { + let message = '

Using a default TURN

Performance is not optimal.'; + const warning = document.createElement('a'); + warning.classList.add('gm-default-turn-button'); + warning.classList.add('gm-icon-button'); + const hover = document.createElement('div'); + hover.className = 'gm-default-turn-used gm-hidden'; + if (this.instance.options.connectionFailedURL) { + message = message + '
Click on the icon to learn more.'; + warning.href = this.instance.options.connectionFailedURL; + warning.target = '_blank'; + } + hover.innerHTML = message; + warning.appendChild(hover); + + warning.onmouseenter = () => { + hover.classList.remove('gm-hidden'); + }; + warning.onmouseleave = () => { + hover.classList.add('gm-hidden'); + }; + const toolbar = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + toolbar.appendChild(warning); + } + + /** + * Hide the default turn warning. + */ + hideDefaultTurnWarning() { + const element = this.instance.getChildByClass(this.instance.root, 'gm-default-turn-button'); + if (element) { + element.remove(); + } + } +}; diff --git a/src/plugins/Phone.js b/src/plugins/Phone.js new file mode 100644 index 00000000..6b4acf70 --- /dev/null +++ b/src/plugins/Phone.js @@ -0,0 +1,222 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +/** + * Instance phone plugin. + * Provides phone call and SMS support. + */ +module.exports = class Phone extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.phone = this; + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-phone-button'; + this.toolbarBtnImage.title = this.i18n.PHONE_TITLE || 'Phone'; + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.container = document.createElement('div'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.PHONE_TITLE || 'Phone'; + this.container.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + + // Phone group + const phoneGroup = document.createElement('div'); + this.phoneInput = document.createElement('input'); + this.phoneBtn = document.createElement('button'); + this.phoneInput.type = 'text'; + this.phoneInput.className = 'gm-phone-number'; + this.phoneInput.placeholder = this.i18n.PHONE_CALL_PLACEHOLDER || 'Please enter the phone number'; + this.phoneInput.addEventListener('keyup', this.validate.bind(this)); + this.phoneBtn.className = 'gm-phone-call'; + this.phoneBtn.innerHTML = this.i18n.PHONE_CALL || 'Call'; + this.phoneBtn.onclick = this.sendPhoneCallToInstance.bind(this); + this.phoneBtn.disabled = true; + + const incomingPhoneLabel = this.i18n.PHONE_INCOMING || 'Incoming phone number'; + + phoneGroup.innerHTML = ''; + phoneGroup.className = 'gm-phone-group'; + phoneGroup.appendChild(this.phoneInput); + phoneGroup.appendChild(this.phoneBtn); + inputs.appendChild(phoneGroup); + + // Text group + const textGroup = document.createElement('div'); + this.textInput = document.createElement('textarea'); + this.textBtn = document.createElement('button'); + this.textInput.className = 'gm-phone-message'; + this.textInput.placeholder = this.i18n.PHONE_MESSAGE_PLACEHOLDER || 'Please enter the incoming message'; + this.textInput.rows = 5; + this.textInput.addEventListener('keyup', this.validate.bind(this)); + this.textBtn.className = 'gm-phone-send'; + this.textBtn.innerHTML = this.i18n.PHONE_MESSAGE || 'Send message'; + this.textBtn.onclick = this.sendSMSToInstance.bind(this); + this.textBtn.disabled = true; + + const messageLabel = this.i18n.PHONE_MESSAGE_VALUE || 'Message'; + textGroup.innerHTML = ''; + textGroup.className = 'gm-phone-group'; + textGroup.appendChild(this.textInput); + textGroup.appendChild(this.textBtn); + inputs.appendChild(textGroup); + + this.container.appendChild(inputs); + + // Setup + this.widget.className = 'gm-overlay gm-phone-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.container); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + */ + toggleWidget() { + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + } + + /** + * Validate phone number format. + * + * @return {boolean} Whether or not phone number format is valid. + */ + validatePhoneNumber() { + const phoneRegex = /^(\+[0-9]{1,14}|[0-9]{1,16})$/g; + return this.phoneInput.value.match(phoneRegex); + } + + /** + * Send phone call event to the instance. + * + * @param {Event} event Event. + */ + sendPhoneCallToInstance(event) { + if (event) { + event.preventDefault(); + } + + if (!this.validatePhoneNumber()) { + return; + } + + const json = {channel: 'baseband', messages: [ + 'gsm call ' + this.phoneInput.value + ]}; + this.instance.sendEvent(json); + this.toggleWidget(); + } + + /** + * Validate SMS text format. + * + * @return {boolean} Whether or not SMS text format is valid. + */ + validateSMSText() { + return this.textInput.value.length > 0; + } + + /** + * Send SMS event to the instance. + * + * @param {Event} event Event. + */ + sendSMSToInstance(event) { + if (event) { + event.preventDefault(); + } + + if (!this.validatePhoneNumber() || !this.validateSMSText()) { + return; + } + + const json = { + channel: 'baseband', messages: [ + 'sms send ' + this.phoneInput.value + ' ' + this.textInput.value + ] + }; + this.instance.sendEvent(json); + this.toggleWidget(); + } + + /** + * Validate all widget inputs. + * + * @return {boolean} Whether or not inputs are valid. + */ + validate() { + const phoneIsValid = this.validatePhoneNumber(); + const textIsValid = this.validateSMSText(); + const valid = textIsValid && phoneIsValid; + + this.phoneBtn.disabled = !phoneIsValid; + this.textBtn.disabled = !valid; + + return valid; + } +}; diff --git a/src/plugins/Screencast.js b/src/plugins/Screencast.js new file mode 100644 index 00000000..be233ea9 --- /dev/null +++ b/src/plugins/Screencast.js @@ -0,0 +1,376 @@ +'use strict'; + +const OverlayPlugin = require('./util/OverlayPlugin'); + +const log = require('loglevel'); +log.setDefaultLevel('debug'); + +const MAX_SCREENCAST_LENGTH_IN_MINUTES = 3; +const CAPTURE_INTERVAL_MS = 50; + +/** + * Instance screencast plugin. + * Provides screenshot and video capture. + */ +module.exports = class Screencast extends OverlayPlugin { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + super(instance); + + // Reference instance + this.instance = instance; + this.i18n = i18n || {}; + + // Register plugin + this.instance.screencast = this; + + // Render components + this.renderToolbarButton(); + this.renderWidget(); + + // Screencast webrtc stuff + this.recordedBlobs = []; + this.mediaRecorder = null; + this.isRecording = false; + this.startTime = 0; + this.displayInterval = null; + } + + /** + * MediaRecorder recorded data available callback. + * + * @param {Event} event Event. + */ + handleDataAvailable(event) { + if (event.data && event.data.size > 0) { + this.recordedBlobs.push(event.data); + } + } + + /** + * Get file extension for a given MIME type. + * + * @param {string} mime MIME type. + * @return {string} File extention + */ + getExtensionFromMimeType(mime) { + const types = { + 'video/x-msvideo': '.avi', + 'video/mpeg': '.mpeg', + 'video/ogg': '.ogv', + 'video/webm': '.webm', + 'video/3gpp': '.3gp', + 'video/3gpp2': '.3g2', + 'video/mp4': '.mp4', + 'video/x-matroska': '.mkv', + 'video/x-flv': '.f4v' + }; + + let extension = types[mime.toLowerCase()]; + if (!extension) { + log.warn('Unknown MIME type: ', mime); + extension = '.webm'; + } + return extension; + } + + /** + * Download recorded screencast. + */ + downloadScreencast() { + const blob = new Blob(this.recordedBlobs, {type: this.mediaRecorder.mimeType}); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'genymotion' + this.getExtensionFromMimeType(this.mediaRecorder.mimeType); + document.body.appendChild(a); + a.click(); + setTimeout(() => { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 100); + this.isRecording = false; + } + + /** + * Update UI based on MediaRecorder availability. + */ + checkForMediarecorder() { + if (typeof MediaRecorder === 'undefined') { + this.screencast.classList.add('disabled'); + this.screencastBtn.title = this.i18n.SCREENCAST_NOMEDIARECORDER_TOOLTIP || 'Screencast not supported'; + } + } + + /** + * Display screencast timer indicator. + */ + displayTimer() { + if (this.timer.classList.contains('gm-timer-hidden')) { + this.timer.classList.remove('gm-timer-hidden'); + this.displayInterval = setInterval(this.displayTimer.bind(this), 1000); + } + + const endTime = new Date(); + + // compute seconds + let timeDiff = endTime - this.startTime; + timeDiff /= 1000; + const seconds = Math.round(timeDiff % 60); + + // compute minutes + timeDiff = Math.floor(timeDiff / 60); + const minutes = Math.round(timeDiff % 60); + + this.timer.innerHTML = ('0' + minutes).slice(-2) + ':' + ('0' + seconds).slice(-2); + + // if the screencast is 3min long, we stop it + if (minutes === MAX_SCREENCAST_LENGTH_IN_MINUTES) { + this.stopRecording(); + } + } + + /** + * Hide screencast timer indicator. + */ + hideTimer() { + clearInterval(this.displayInterval); + this.timer.classList.add('gm-timer-hidden'); + this.timer.innerHTML = ''; + } + + findBestMimeType() { + const supportedMimeTypes = []; + [ + 'video/webm', + 'video/x-matroska', + 'video/mpeg', + 'video/ogg', + 'video/3gpp', + 'video/3gpp2', + 'video/mp4', + 'video/x-flv' + + ].forEach((mimeType) => { + if (MediaRecorder.isTypeSupported(mimeType)) { + supportedMimeTypes.push(mimeType); + } + }); + + return supportedMimeTypes[0]; + } + + /** + * Start screencast recording. + */ + startRecording() { + try { + this.mediaRecorder = new MediaRecorder(this.instance.stream, { + mimeType: this.findBestMimeType(), + }); + } catch (error) { + log.error('Error while creating MediaRecorder: ' + error); + this.hideTimer(); + this.toolbarBtnImage.classList.remove('gm-screencast-button-recording'); + return; + } + + this.recordedBlobs = []; + this.isRecording = true; + this.startTime = new Date(); + this.displayTimer(); + this.mediaRecorder.ondataavailable = this.handleDataAvailable.bind(this); + this.mediaRecorder.start(CAPTURE_INTERVAL_MS); + log.debug('MediaRecorder started', this.mediaRecorder); + } + + /** + * Stop screencast recording. + */ + stopRecording() { + this.mediaRecorder.stop(); + this.hideTimer(); + this.toolbarBtnImage.classList.remove('gm-screencast-button-recording'); + this.downloadScreencast(); + log.debug('MediaRecorder stopped', this.mediaRecorder); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.toolbarBtn = document.createElement('li'); + this.toolbarBtnImage = document.createElement('div'); + this.toolbarBtnImage.className = 'gm-icon-button gm-screencast-button'; + this.toolbarBtnImage.title = this.i18n.SCREENCAST_TITLE || 'Screencast'; + this.timer = document.createElement('div'); + this.timer.className = 'gm-screencast-timer gm-timer-hidden'; + this.toolbarBtnImage.appendChild(this.timer); + this.toolbarBtn.appendChild(this.toolbarBtnImage); + this.toolbarBtn.onclick = this.toggleWidget.bind(this); + toolbar.appendChild(this.toolbarBtn); + } + + /** + * Render the widget. + */ + renderWidget() { + // Create elements + this.widget = document.createElement('div'); + this.form = document.createElement('form'); + + // Generate title + const title = document.createElement('div'); + title.className = 'gm-title'; + title.innerHTML = this.i18n.SCREENCAST_TITLE || 'Screencast'; + this.form.appendChild(title); + + // Generate input rows + const inputs = document.createElement('div'); + inputs.className = 'gm-inputs'; + + const screenshot = document.createElement('div'); + this.screenshotBtn = document.createElement('div'); + this.screenshotBtn.className = 'gm-action gm-screencast-screenshot'; + screenshot.onclick = this.onScreenshotClick.bind(this); + screenshot.className = 'gm-horizontal gm-screenshot'; + + const screenshotLabel = this.i18n.SCREENCAST_SCREENSHOT || 'Screenshot'; + screenshot.innerHTML = ''; + + screenshot.appendChild(this.screenshotBtn); + inputs.appendChild(screenshot); + + this.screencast = document.createElement('div'); + this.screencastBtn = document.createElement('div'); + this.screencastBtn.className = 'gm-action gm-screencast-screencast'; + + this.screencast.onclick = this.onScreencastClick.bind(this); + this.screencast.className = 'gm-horizontal gm-screencast'; + + const screencastLabel = this.i18n.SCREENCAST_SCREENCAST || 'Screencast'; + this.screencast.innerHTML = ''; + + this.screencast.appendChild(this.screencastBtn); + inputs.appendChild(this.screencast); + + this.form.appendChild(inputs); + + // Setup + this.widget.className = 'gm-overlay gm-screencast-plugin gm-hidden'; + + // Add close button + const close = document.createElement('div'); + close.className = 'gm-close-btn'; + close.onclick = this.toggleWidget.bind(this); + + this.widget.appendChild(close); + this.widget.appendChild(this.form); + + // Render into document + this.overlays.push(this.widget); + this.instance.root.appendChild(this.widget); + } + + /** + * Display or hide the widget. + * + * @param {boolean} keepIconActive Whether to keep plugin button active or not. + */ + toggleWidget(keepIconActive) { + this.checkForMediarecorder(); + + // When we are recording, we don't open the widget + if (this.isRecording) { + this.stopRecording(); + return; + } + + // Notify other callers + if (this.widget.classList.contains('gm-hidden')) { + this.instance.emit('close-overlays'); + this.instance.emit('keyboard-disable'); + } else { + this.instance.emit('keyboard-enable'); + } + + // Toggle display + this.widget.classList.toggle('gm-hidden'); + this.toolbarBtnImage.classList.toggle('gm-active'); + + if (keepIconActive === true) { + this.toolbarBtnImage.classList.add('gm-screencast-button-recording'); + } + } + + /** + * Take a screenshot. + * + * @param {Event} event Event. + */ + onScreenshotClick(event) { + event.preventDefault(); + + this.toggleWidget(); + + // Setup + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const {video} = this.instance; + + // Set canvas size to render to + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + + // Render into the canvas the current state of video + if (ctx && video instanceof HTMLVideoElement) { + ctx.drawImage(video, 0, 0, video.videoWidth, + video.videoHeight, 0, 0, video.videoWidth, video.videoHeight); + } else { + return; + } + + // configure download link + const downloadLink = document.createElement('a'); + downloadLink.download = 'genymotion-screenshot.png'; + document.body.appendChild(downloadLink); + + // Get canvas data / force download + let data = canvas.toDataURL('image/png'); + if (data) { + data = data.replace(/^data:image\/[^;]*/, 'data:application/octet-stream'); + } + downloadLink.href = data; + + // Trigger download + downloadLink.click(); + + setTimeout(() => { + document.body.removeChild(downloadLink); + }, 100); + } + + /** + * Toggle screencast recording. + */ + onScreencastClick() { + if (!this.isRecording) { + this.toggleWidget(true); + this.startRecording(); + } else { + this.stopRecording(); + } + } +}; diff --git a/src/plugins/StreamBitrate.js b/src/plugins/StreamBitrate.js new file mode 100644 index 00000000..2cb70859 --- /dev/null +++ b/src/plugins/StreamBitrate.js @@ -0,0 +1,59 @@ +'use strict'; + +/** + * Stream bitrate plugin. + * Provides video stream quality (audio and video bitrates) control. + */ +module.exports = class StreamBitrate { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.StreamBitrate = this; + this.i18n = i18n || {}; + + this.highQuality = false; + + // Display widget + this.renderToolbarButton(); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + const button = document.createElement('li'); + this.chooser = document.createElement('div'); + this.chooser.className = '.gm-streamrate-chooser gm-icon-button gm-streamrate-button'; + this.chooser.title = this.i18n.STREAMRATE_TITLE || 'High quality'; + button.appendChild(this.chooser); + toolbar.appendChild(button); + this.chooser.onclick = () => { + this.highQuality = !this.highQuality; + this.chooser.classList.toggle('gm-active'); + if (this.highQuality) { + const json = {type: 'BITRATE', videoBitrate: 5000, audioBitrate: 192000}; + this.instance.sendEvent(json); + this.instance.renegotiateWebRTCConnection(); + } else { + // if we pass 0 here, we just use WebRTC default value + const json = {type: 'BITRATE', videoBitrate: 0, audioBitrate: 0}; + this.instance.sendEvent(json); + this.instance.renegotiateWebRTCConnection(); + } + }; + } +}; diff --git a/src/plugins/StreamResolution.js b/src/plugins/StreamResolution.js new file mode 100644 index 00000000..547467de --- /dev/null +++ b/src/plugins/StreamResolution.js @@ -0,0 +1,63 @@ +'use strict'; + +const RESOLUTIONS = [ + {text: '-', value: '0'}, + {text: '240p', value: '240'}, + {text: '360p', value: '360'}, + {text: '480p', value: '480'}, + {text: '720p', value: '720'}, + {text: '1080p', value: '1080'}, +]; + +/** + * Stream resolution plugin. + * Provides video stream resolution control. + */ +module.exports = class StreamResolution { + /** + * Plugin initialization. + * + * @param {Object} instance Associated instance. + * @param {Object} i18n Translations keys for the UI. + */ + constructor(instance, i18n) { + // Reference instance + this.instance = instance; + + // Register plugin + this.instance.StreamResolution = this; + this.i18n = i18n || {}; + + // Display widget + this.renderToolbarButton(); + } + + /** + * Add the button to the player toolbar. + */ + renderToolbarButton() { + const toolbars = this.instance.getChildByClass(this.instance.root, 'gm-toolbar'); + if (!toolbars) { + return; // if we don't have toolbar, we can't spawn the widget + } + + const toolbar = toolbars.children[0]; + this.select = document.createElement('select'); + this.select.className = 'gm-icon-button gm-streamres-button'; + this.select.title = this.i18n.STREAMRES_TITLE || 'Quality'; + RESOLUTIONS.forEach((resolution) => { + this.select.add(new Option(resolution.text, resolution.value)); + }); + this.select.onchange = this.onStreamResolutionChange.bind(this); + toolbar.appendChild(this.select); + } + + /** + * Apply the selected stream resolution. + */ + onStreamResolutionChange() { + const json = {type: 'SIZE', width: Number(this.select.value)}; + this.instance.sendEvent(json); + this.select.blur(); + } +}; diff --git a/src/plugins/util/OverlayPlugin.js b/src/plugins/util/OverlayPlugin.js new file mode 100644 index 00000000..925f9745 --- /dev/null +++ b/src/plugins/util/OverlayPlugin.js @@ -0,0 +1,94 @@ +'use strict'; + +/** + * OverlayPlugin + * Extendable utility to auto-bind and handle genymotion 'closeOverlays' event. + * Expects implementer to provide list of overlays + */ +module.exports = class OverlayPlugin { + /** + * Plugin initialization + * @param {Object} instance Genymotion player instance + */ + constructor(instance) { + this.overlays = []; + this.savedState = null; + this.toolbarBtnImage = null; + this.instance = instance; + + // Listen for close trigger + this.instance.registerEventCallback('close-overlays', this.closeOverlays.bind(this)); + } + + /** + * Closes all active overlays and updates toolbar button state + */ + closeOverlays() { + this.overlays.forEach((overlay) => { + if (overlay && !overlay.classList.contains('gm-hidden')) { + overlay.classList.add('gm-hidden'); + if (overlay.onclose) { + overlay.onclose(); + } + } + }); + + this.instance.emit('keyboard-enable'); + + if (this.toolbarBtnImage) { + this.toolbarBtnImage.classList.remove('gm-active'); + } + } + + /** + * Save current button state (icon & click action) + */ + saveState() { + if (!this.savedState) { + this.savedState = { + toolbarBtn: { + className: this.toolbarBtn.className, + onclick: this.toolbarBtn.onclick, + }, + toolbarBtnImage: { + className: this.toolbarBtnImage.className, + }, + }; + } + } + + /** + * Re-apply the saved toolbar button state. + */ + restoreState() { + if (this.savedState) { + this.toolbarBtn.className = this.savedState.toolbarBtn.className; + this.toolbarBtnImage.className = this.savedState.toolbarBtnImage.className; + this.toolbarBtn.onclick = this.savedState.toolbarBtn.onclick; + + this.savedState = null; + } + } + + /** + * Disable associated toolbar icon. + */ + disable() { + if (this.toolbarBtn && this.toolbarBtnImage) { + this.saveState(); + + this.toolbarBtn.className += ' gm-disabled-widget-pop-up'; + this.toolbarBtnImage.className += ' gm-disabled-widget-icon'; + this.toolbarBtn.onclick = null; + } + } + + /** + * Enable associated toolbar icon. + */ + enable() { + if (this.toolbarBtn && this.toolbarBtnImage) { + this.restoreState(); + } + } +}; diff --git a/src/plugins/util/iothrottling-profiles.js b/src/plugins/util/iothrottling-profiles.js new file mode 100644 index 00000000..0186e8b6 --- /dev/null +++ b/src/plugins/util/iothrottling-profiles.js @@ -0,0 +1,27 @@ +'use strict'; + +/** + * List of available IO throttling profiles to send to VM + * + * readByteRate: The total rate in MegaBytes of read per second + * writeByteRate: The total rate in MegaBytes of write per second + * readIoRate: The number of read operation allowed per second + * writeIoRate: The number of write operation allowed per second + * + */ +module.exports = [{ + name: 'High-end device', + label: 'High-end device', + readByteRate: 200, +}, { + name: 'Mid-range device', + label: 'Mid-range device', + readByteRate: 100, +}, { + name: 'Low-end device', + label: 'Low-end device', + readByteRate: 50, +}, { + name: 'Custom', + label: 'Custom device', +}]; diff --git a/src/plugins/util/network-profiles.js b/src/plugins/util/network-profiles.js new file mode 100644 index 00000000..284a775f --- /dev/null +++ b/src/plugins/util/network-profiles.js @@ -0,0 +1,294 @@ +'use strict'; + +/** + * List of available network profiles to send to VM + */ +module.exports = [{ + id: 8, + name: 'wifi', + label: 'Wifi', + downSpeed: { + label: '40.0Mb/s', + value: 40000, + }, + downDelay: { + label: '0ms', + value: 0, + }, + downPacketLoss: { + label: '0%', + value: 0.0, + }, + upSpeed: { + label: '33.0Mb/s', + value: 33000, + }, + upDelay: { + label: '0ms', + value: 0, + }, + upPacketLoss: { + label: '0%', + value: 0.0, + }, + dnsDelay: { + label: '0ms', + value: 0, + }, +}, { + id: 7, + name: '4g_with_loss', + label: '4G (High packet losses)', + downSpeed: { + label: '17.9Mb/s', + value: 17900, + }, + downDelay: { + label: '50ms', + value: 50, + }, + downPacketLoss: { + label: '10%', + value: 10.0, + }, + upSpeed: { + label: '5.5Mb/s', + value: 5500, + }, + upDelay: { + label: '50ms', + value: 50, + }, + upPacketLoss: { + label: '10%', + value: 10.0, + }, + dnsDelay: { + label: '100ms', + value: 100, + }, +}, { + id: 6, + name: '4g_with_delay', + label: '4G (High DNS delay)', + downSpeed: { + label: '17.9Mb/s', + value: 17900, + }, + downDelay: { + label: '50ms', + value: 50, + }, + downPacketLoss: { + label: '0.01%', + value: 0.01, + }, + upSpeed: { + label: '5.5Mb/s', + value: 5500, + }, + upDelay: { + label: '50ms', + value: 50, + }, + upPacketLoss: { + label: '0.01%', + value: 0.01, + }, + dnsDelay: { + label: '3000ms', + value: 3000, + }, +}, { + id: 5, + name: '4g', + label: '4G', + downSpeed: { + label: '17.9Mb/s', + value: 17900, + }, + downDelay: { + label: '50ms', + value: 50, + }, + downPacketLoss: { + label: '0.01%', + value: 0.01, + }, + upSpeed: { + label: '5.5Mb/s', + value: 5500, + }, + upDelay: { + label: '50ms', + value: 50, + }, + upPacketLoss: { + label: '0.01%', + value: 0.01, + }, + dnsDelay: { + label: '100ms', + value: 100, + }, +}, { + id: 4, + name: '3g', + label: '3G', + downSpeed: { + label: '7.2Mb/s', + value: 7200, + }, + downDelay: { + label: '100ms', + value: 100, + }, + downPacketLoss: { + label: '0.01%', + value: 0.01, + }, + upSpeed: { + label: '1.5Mb/s', + value: 1500, + }, + upDelay: { + label: '100ms', + value: 100, + }, + upPacketLoss: { + label: '0.01%', + value: 0.01, + }, + dnsDelay: { + label: '200ms', + value: 200, + }, +}, { + id: 3, + name: 'edge', + label: 'EDGE', + downSpeed: { + label: '240Kb/s', + value: 240, + }, + downDelay: { + label: '400ms', + value: 400, + }, + downPacketLoss: { + label: '0.01%', + value: 0.01, + }, + upSpeed: { + label: '200Kb/s', + value: 200, + }, + upDelay: { + label: '400ms', + value: 400, + }, + upPacketLoss: { + label: '0.01%', + value: 0.01, + }, + dnsDelay: { + label: '800ms', + value: 800, + }, +}, { + id: 2, + name: 'gprs', + label: 'GPRS', + downSpeed: { + label: '40Kb/s', + value: 40, + }, + downDelay: { + label: '500ms', + value: 500, + }, + downPacketLoss: { + label: '0.01%', + value: 0.01, + }, + upSpeed: { + label: '40Kb/s', + value: 40, + }, + upDelay: { + label: '500ms', + value: 500, + }, + upPacketLoss: { + label: '0.01%', + value: 0.01, + }, + dnsDelay: { + label: '1000ms', + value: 1000, + }, +}, { + id: 1, + name: 'no_data', + label: 'No data', + downSpeed: { + label: '0Kb/s', + value: 0, + }, + downDelay: { + label: '0ms', + value: 0, + }, + downPacketLoss: { + label: '0%', + value: 0.0, + }, + upSpeed: { + label: '0Kb/s', + value: 0, + }, + upDelay: { + label: '0ms', + value: 0, + }, + upPacketLoss: { + label: '0%', + value: 0.0, + }, + dnsDelay: { + label: '0ms', + value: 0, + }, +}, { + id: 0, + name: 'native', + label: 'Native', + downSpeed: { + label: 'N/A', + value: Infinity, + }, + downDelay: { + label: 'N/A', + value: 0, + }, + downPacketLoss: { + label: 'N/A', + value: 0, + }, + upSpeed: { + label: 'N/A', + value: Infinity, + }, + upDelay: { + label: 'N/A', + value: 0, + }, + upPacketLoss: { + label: 'N/A', + value: 0, + }, + dnsDelay: { + label: 'N/A', + value: 0, + }, +}]; diff --git a/src/scss/base/_genymotion.scss b/src/scss/base/_genymotion.scss new file mode 100644 index 00000000..535ae5b7 --- /dev/null +++ b/src/scss/base/_genymotion.scss @@ -0,0 +1,119 @@ +/** + * Common player style + */ +.gm-player-instance { + text-align: center; + + *, *::after, *::before { + box-sizing: border-box; + } + + .invisible { + opacity: 0; + } + + .hidden { + display: none !important; + } + + &:focus { + outline: 0; + } + + ul { + list-style-type: none; + padding: 0; + margin: 0; + + li img { + width: 24px; + } + } + + .gm { + &-video { + margin: 0; + max-width: 100%; + } + + &-icon-button { + display: block; + width: 32px; + height: 26px; + margin: 16px auto 16px auto; + } + + &-video-overlay { + position: absolute; + z-index: 1; + cursor: pointer; + background-color: $black; + color: $white; + text-align: center; + display: block; + width: calc(100% - 55px); + height: 100%; + left: 0; + top: 0; + } + + &-click-to-display { + background: black url("../assets/images/ic_click_to_display_default.svg") no-repeat center; + background-size: calc(150px + 10%); + + &:hover { + background-image: url("../assets/images/ic_click_to_display_hover.svg"); + } + } + + &-click-to-unmute { + background-color: darken(#223140, 15%); + background: darken(#223140, 15%) url("../assets/images/ic_icon_mute.svg") no-repeat center; + background-position: 15px 20px; + background-size: 30px; + border-radius: 8px; + font-size: 15px; + text-align: left; + font-family: Roboto, sans-serif; + padding: 16px 20px 17px 60px; + position: absolute; + z-index: 1; + color: $white; + display: block; + width: 80%; + max-width: 380px; + min-height: 66px; + left: calc(10% - 30px); + top: calc(86% - 34px); + cursor: pointer; + @media screen and (min-width: 475px) { + left: calc(50% - 220px); + } + } + + &-overlay-cant-connect { + background: black url("../assets/images/ic_error_default.svg") no-repeat center 40%; + background-size: 200px; + font-size: 28px; + font-family: Roboto, sans-serif; + font-weight: 300; + padding-top: 10%; + + .gm-error-text { + position: absolute; + top: calc(40% + 140px); + width: 90%; + padding-left: 10%; + } + + p { + font-size: 50%; + font-weight: normal; + } + + a { + color: $btn-background-color; + } + } + } +} diff --git a/src/scss/base/_variables.scss b/src/scss/base/_variables.scss new file mode 100644 index 00000000..252a89d2 --- /dev/null +++ b/src/scss/base/_variables.scss @@ -0,0 +1,19 @@ +/** + * SASS variables + */ + +// General use +$white: #ffffff; +$black: #000000; +$accent-color-light: #e6195e; + +// Responsive breakpoint +$breakpoint-mobile: 650px; + +// Elements +$btn-background-color: #e6195e; +$btn-background-color-hover: #c62c53; +$btn-disabled-background: rgba(179, 179, 179, 0.24); +$btn-disabled-color: #c4c4c4; +$btn-disabled-background-hover: #828282; +$modal-background-color: #303339; diff --git a/src/scss/components/_battery.scss b/src/scss/components/_battery.scss new file mode 100644 index 00000000..7be926f2 --- /dev/null +++ b/src/scss/components/_battery.scss @@ -0,0 +1,79 @@ +/** + * Battery plugin styles + */ +.gm-player-instance .gm-battery-plugin { + .gm { + &-charging-group { + margin-top: 15px; + + .gm-charging-checkbox { + width: 15px; + float: left; + margin: 0; + } + + .gm-charging-status { + height: 32px; + line-height: 32px; + font-weight: bold; + margin-left: 20px; + + &.charging { + color: $accent-color-light; + } + } + } + + &-charge-level-group { + height: 75px; + display: flex; + justify-content: space-around; + align-items: center; + + * { + width: 33%; + text-align: center; + } + + .gm-charge-image { + position: relative; + width: 35px; + height: 60px; + background: url("../assets/images/ic-battery_empty.svg") no-repeat center; + background-size: 32px; + margin: 0 auto; + + .gm-charge-image-overlay { + position: absolute; + left: 10%; + bottom: 3px; + width: 80%; + height: 0; + background-color: $white; + } + + .gm-full-charge-image-overlay { + position: absolute; + left: 37%; + bottom: 10px; + width: 27%; + height: 0; + background-color: $white; + } + } + + .gm-charge-input { + width: 80px; + margin: 0 4px 0 0; + } + + .gm-charge-slider { + margin: 0; + width: 65px; + height: 31px; + transform: rotate(270deg); + padding: 0 3px; + } + } + } +} diff --git a/src/scss/components/_clipboard.scss b/src/scss/components/_clipboard.scss new file mode 100644 index 00000000..f1d3c148 --- /dev/null +++ b/src/scss/components/_clipboard.scss @@ -0,0 +1,19 @@ +/** + * Clipboard plugin styles + */ +.gm-player-instance .gm-clipboard-plugin { + .gm { + &-clipboard-input { + min-height: 300px; + resize: none; + color: $black !important; + background-color: $white !important; + } + + &-text-copied { + padding: 15px 0 0 30px; + transition: opacity 0.5s ease-in-out; + background: url("../assets/images/ic_clipboard_inactive.svg") no-repeat left 10px !important; + } + } +} diff --git a/src/scss/components/_fullscreen.scss b/src/scss/components/_fullscreen.scss new file mode 100644 index 00000000..97e7d40e --- /dev/null +++ b/src/scss/components/_fullscreen.scss @@ -0,0 +1,36 @@ +/** + * Fullscreen plugin styles + */ +.gm-player-instance .gm-fullscreen { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0; + + .gm { + &-player { + background: $black !important; + height: 100%; + max-height: 100% !important; + } + + &-video { + position: absolute; + top: 0; + left: 0; + width: calc(100% - 60px); + height: 100%; + padding: 0; + } + + &-toolbars { + position: absolute; + top:0; + right: 0; + width: 48px; + height: 100%; + } + } +} diff --git a/src/scss/components/_gps.scss b/src/scss/components/_gps.scss new file mode 100644 index 00000000..8aa2f547 --- /dev/null +++ b/src/scss/components/_gps.scss @@ -0,0 +1,50 @@ +/** + * GPS plugin styles + */ +.gm-player-instance .gm-gps { + &-controls { + label { + margin-top: 10px; + } + + input { + &.gm-error { + border-bottom: 1px solid $btn-background-color; + } + } + } + + &-submit { + margin-top: 10px; + } + + &-geoloc { + margin-top: 10px; + } + + &-mapview { + height: 460px; + + .gm-mapview { + position: absolute; + bottom: 76px; + left: 26px; + right: 26px; + top: 26px; + background-color: $white; + } + + .gm-gps-mapview-capture { + position: absolute; + bottom: 20px; + left: calc(50% + 80px); + } + + .gm-gps-mapview-cancel { + position: absolute; + bottom: 20px; + right: calc(50% + 80px); + } + } + +} diff --git a/src/scss/components/_imei.scss b/src/scss/components/_imei.scss new file mode 100644 index 00000000..2879a36e --- /dev/null +++ b/src/scss/components/_imei.scss @@ -0,0 +1,30 @@ +/** + * Identifiers (IMEI) plugin styles + */ +.gm-player-instance .gm-identifiers-plugin { + .gm-identifier-device, .gm-identifier-android { + .gm-description { + font-size: 90%; + padding-left: 10px; + } + + margin-bottom: 16px; + + .gm-input-group { + display: flex; + + button { + margin-top: -5px; + } + } + } + + input { + margin-right: 6px; + flex: 1; + + &.gm-error { + border-bottom: 1px solid $btn-background-color; + } + } +} diff --git a/src/scss/components/_iothrottling.scss b/src/scss/components/_iothrottling.scss new file mode 100644 index 00000000..a53c6da6 --- /dev/null +++ b/src/scss/components/_iothrottling.scss @@ -0,0 +1,57 @@ +/** + * IO Throttling plugin styles + */ +.gm-player-instance .gm-iothrottling-plugin { + .gm-inputs { + margin-bottom: 15px; + + select { + font-size: medium; + width: 100%; + margin-top:10px; + padding: 2px; + } + } + + label { + margin-top: 20px; + display: block; + float: left; + } + + .gm-fields { + display: block; + margin: 0; + padding: 0; + width: 100%; + height: 50px; + } + + .gm-units { + text-align: right; + float: right; + width: 160px; + } + + input { + font-size: medium; + font-family: Helvetica,sans-serif; + margin-top: 10px; + width: 30%; + text-align: right; + float: right; + + &:read-only { + border-bottom: 1px solid transparent; + background: $btn-disabled-background; + } + + &:required:invalid, &:focus:invalid { + border-bottom: 1px solid $btn-background-color; + } + } + + button { + margin-top: 20px; + } +} diff --git a/src/scss/components/_network.scss b/src/scss/components/_network.scss new file mode 100644 index 00000000..c1ad606c --- /dev/null +++ b/src/scss/components/_network.scss @@ -0,0 +1,60 @@ +/** + * Network plugin styles + */ +.gm-player-instance .gm-network-plugin { + .gm { + &-inputs { + margin-bottom: 15px; + + select { + width: 100%; + margin-top: 10px; + padding: 2px; + } + } + + &-profile-details { + margin: 12px 0; + } + + &-section { + display: block; + margin: 20px 0 10px 0; + padding: 0; + width: 100%; + height: 50px; + font-weight: bold; + border-bottom: 1px solid $white; + } + + &-fields { + display: block; + padding: 0; + width: 100%; + height: 50px; + } + } + + label { + margin-top: 20px; + display: block; + float: left; + } + + input { + margin-top: 10px; + width: 60%; + float: right; + text-align: right; + font-size: medium; + font-family: Helvetica, sans-serif; + + &:invalid, &:focus:invalid { + border-bottom: 1px solid $btn-background-color; + } + } + + button { + margin-top: 20px; + } +} diff --git a/src/scss/components/_phone.scss b/src/scss/components/_phone.scss new file mode 100644 index 00000000..c49b4926 --- /dev/null +++ b/src/scss/components/_phone.scss @@ -0,0 +1,19 @@ +/** + * Phone plugin styles + */ +.gm-player-instance .gm-phone-plugin { + .gm-phone-group { + &:first-child { + margin-bottom: 12px; + } + } + + input, textarea { + margin-bottom: 4px; + } + + textarea { + resize: none; + width: 100%; + } +} diff --git a/src/scss/components/_screencast.scss b/src/scss/components/_screencast.scss new file mode 100644 index 00000000..5584a81c --- /dev/null +++ b/src/scss/components/_screencast.scss @@ -0,0 +1,90 @@ +/** + * Screencast plugin styles + */ +.gm-player-instance { + .gm-screencast-plugin { + width: 400px; + left: calc(50% - 200px); + + /* Extra Small Devices, Tablet or Phones */ + @media only screen and (max-width : $breakpoint-mobile) { + max-width: 400px; + } + + label { + height: 32px; + line-height: 35px; + margin-bottom: 0; + margin-left: 20px; + cursor: pointer; + } + + .disabled { + opacity: 0.4; + pointer-events: none; + } + + .gm-screenshot, .gm-screencast { + display: inline-block; + width: auto; + margin-right: 50px; + + .gm-action { + width: 32px; + min-height: 32px; + cursor: pointer; + display: block; + float: left; + background-size: 26px; + background-repeat: no-repeat; + background-position: center; + } + } + + .gm-screenshot .gm-action { + background-image: url("../assets/images/ic-screenshot_default.svg"); + + } + + .gm-screencast .gm-action { + background-image: url("../assets/images/ic-screencast_default.svg"); + + .disabled { + background-image: url("../assets/images/ic-screencast_disabled.svg"); + color: $btn-disabled-background; + } + + } + } + + .gm-screencast-timer { + position: relative; + top: -10px; + left: -90px; + color: #c6225a; + font-size: 20px; + font-family: Helvetica, sans-serif; + pointer-events: none; + z-index: 5; + padding: 11px 8px 8px 8px; + border-radius: 6px; + width: 80px; + background-color: $modal-background-color; + vertical-align: middle; + + &::after { + content: " "; + position: absolute; + top: 50%; + left: 100%; /* To the right of the tooltip */ + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent transparent transparent $modal-background-color; + } + } + + .gm-timer-hidden { + display: none; + } +} \ No newline at end of file diff --git a/src/scss/components/_toolbar.scss b/src/scss/components/_toolbar.scss new file mode 100644 index 00000000..8e146d7d --- /dev/null +++ b/src/scss/components/_toolbar.scss @@ -0,0 +1,186 @@ +/** + * Player toolbar styles + */ +.gm-player-instance { + .gm-toolbar img:hover, + .gm-streamrate-chooser img:hover { + cursor: pointer; + } + + .gm { + &-icon-button { + background-size: contain; + background-repeat: no-repeat; + background-position: center; + &:hover { + cursor: pointer; + } + } + + &-rotation { + background-image: url("../assets/images/ic_rotation_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_rotation_active.svg"); + } + } + + &-sound-up { + background-image: url("../assets/images/ic_sound_up_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_sound_up_active.svg"); + } + } + + &-sound-down { + background-image: url("../assets/images/ic_sound_down_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_sound_down_active.svg"); + } + } + + &-recent { + background-image: url("../assets/images/ic-nav_android_multiapp_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic-nav_android_multiapp_active.svg"); + } + } + + &-home { + background-image: url("../assets/images/ic-nav_android_home_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic-nav_android_home_active.svg"); + } + } + + &-back { + background-image: url("../assets/images/ic-nav_android_back_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic-nav_android_back_active.svg"); + } + } + + &-battery-button { + background-image: url("../assets/images/ic-battery_inactive.svg"); + &:hover, &.gm-active { + background-image: url("../assets/images/ic-battery_active.svg"); + } + } + + &-power { + background-image: url("../assets/images/ic_icon_power_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_icon_power_active.svg"); + } + } + + &-camera-button { + background-image: url("../assets/images/ic_camera_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_camera_active.svg"); + } + } + + &-streamres-button { + background-image: url("../assets/images/ic-resolution_inactive.svg"); + background-color: transparent; + outline: none; + border: none; + color :transparent; + -webkit-appearance: none; + -moz-appearance: none; + + &:focus { + outline-style: none; + box-shadow: none; + border-color: transparent; + color: $white; + background: $black !important; + } + + &:hover { + background-image: url("../assets/images/ic-resolution_active.svg"); + } + } + + &-gps-button, &-gps-button.gm-active { + background-image: url("../assets/images/ic_location_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_location_active.svg"); + } + } + + &-fullscreen-button { + background-image: url("../assets/images/ic_fullscreen_inactive.svg"); + &:hover { + background-image: url("../assets/images/ic_fullscreen_active.svg"); + } + &.gm-active, &.gm-active:hover { + background-image: url("../assets/images/ic_fullscreen_exit_active.svg"); + } + } + + &-screencast-button { + background-image: url("../assets/images/ic-screenshot_inactive.svg"); + &:hover, &.gm-active, &.gm-active:hover { + background-image: url("../assets/images/ic-screenshot_active.svg"); + } + } + + &-screencast-button-recording { + background-image: url("../assets/images/ic-screenshot_active.svg"); + } + + &-identifiers-button { + background-image: url("../assets/images/ic_id_inactive.svg"); + &:hover, &.gm-active, &.gm-active:hover { + background-image: url("../assets/images/ic_id_active.svg"); + } + } + + &-network-button { + background-image: url("../assets/images/ic_network_inactive.svg"); + &:hover, &.gm-active, &.gm-active:hover { + background-image: url("../assets/images/ic_network_active.svg"); + } + } + + &-iothrottling-button { + background-image: url("../assets/images/ic_diskIO_inactive.svg"); + &:hover, &.gm-active, &.gm-active:hover { + background-image: url("../assets/images/ic_diskIO_active.svg"); + } + } + + &-phone-button { + background-image: url("../assets/images/ic_textandcall_inactive.svg"); + &:hover, &.gm-active { + background-image: url("../assets/images/ic_textandcall_active.svg"); + } + } + + &-uploader-button { + background-image: url("../assets/images/ic_installation_inactive.svg"); + &:hover, &.gm-active { + background-image: url("../assets/images/ic_installation_active.svg"); + } + } + + &-streambitrate-button { + background-image: url("../assets/images/hq.png"); + &:hover, &.gm-active { + background-image: url("../assets/images/hq-over.png"); + } + } + + &-clipboard-button { + background-image: url("../assets/images/ic_clipboard_inactive.svg"); + &:hover, &.gm-active { + background-image: url("../assets/images/ic_clipboard_active.svg"); + } + } + + &-default-turn-button { + background-image: url("../assets/images/ic_warning.svg"); + } + } +} diff --git a/src/scss/components/_turn.scss b/src/scss/components/_turn.scss new file mode 100644 index 00000000..83d22d94 --- /dev/null +++ b/src/scss/components/_turn.scss @@ -0,0 +1,49 @@ +/** + * Turn server plugin styles + */ + +.gm-player-instance .gm-default-turn-button { + text-decoration: none; + + .gm-default-turn-used { + position: relative; + top: -48px; + left: -305px; + color: $white; + font-size: 1em; + font-family: Helvetica, sans-serif; + pointer-events: none; + z-index: 5; + border-radius: 6px; + width: 290px; + background-color: $modal-background-color; + vertical-align: middle; + padding: 11px 8px 20px; + + &::after { + content: " "; + position: absolute; + top: 50%; + left: 100%; /* To the right of the tooltip */ + margin-top: -10px; + border-width: 10px; + border-style: solid; + border-color: transparent transparent transparent $modal-background-color; + } + + h1 { + font-size: 1.1em; + color: $accent-color-light; + padding-bottom: 5px; + + span { + font-weight: bolder; + font-size: 1.2em; + } + } + } + + .gm-hidden { + display: none; + } +} diff --git a/src/scss/components/_upload.scss b/src/scss/components/_upload.scss new file mode 100644 index 00000000..deb694f1 --- /dev/null +++ b/src/scss/components/_upload.scss @@ -0,0 +1,209 @@ +/** + * Upload plugin styles + */ +.gm-player-instance { + .gm { + &-wrapper { + display: inline-block; + border: 10px solid transparent; + } + + &-dragover { + border: 10px solid $accent-color-light; + } + + &-upload-progress { + display: block; + position: absolute; + left: 5%; + top: 5%; + z-index: 1; + } + + &-custom-file-upload { + display: inline; + } + + &-upload-main { + text-align: center; + + &-img { + width: 58px; + margin-top: 30px; + } + + &-txt { + margin-top: 15px; + font-size: 16px; + } + } + + &-upload-disclaimer { + &-txt { + height: 336px; + overflow: auto; + margin-top: 30px; + } + + &-txt::-webkit-scrollbar { + width: 20px; + } + + &-txt::-webkit-scrollbar-track { + background: #45474D; + border-left: 9px solid $modal-background-color; + border-right: 9px solid $modal-background-color; + } + + &-txt::-webkit-scrollbar-thumb { + background: white; + border-left: 8px solid $modal-background-color; + border-right: 8px solid $modal-background-color; + border-radius: 9px; + } + } + + &-upload-in-progress { + margin-top: 30px; + padding-right: 30px; + padding-left: 30px; + + &-txt-progress { + display: inline-block; + } + + &-txt-percent { + display: block; + float: right; + } + + &-bg-percent { + height: 3px; + background-color: #D8D8D8; + margin-top: 5px; + } + + &-percent { + height: 3px; + background-color: $btn-background-color; + } + } + + &-upload-error { + text-align: center; + margin-top: 30px; + + &-img { + width: 58px; + } + + &-caption { + font-size: 16px; + margin-top: 15px; + margin-bottom: 30px; + } + + &-button { + margin-top: 26px; + display: inline-block; + padding: 10px; + text-transform: uppercase; + font-weight: bold; + font-size: 14px; + cursor: pointer; + color: $white; + background-color: $btn-background-color; + border-radius: 2px; + margin-bottom: 10px; + + &:hover { + background-color: $btn-background-color-hover; + color: $white; + } + } + } + + &-upload-success { + text-align: center; + margin-top: 30px; + + &-img { + width: 58px; + } + + &-txt { + font-size: 16px; + } + + &-caption { + font-size: 16px; + margin-top: 15px; + margin-bottom: 30px; + } + + &-button { + margin-top: 26px; + display: inline-block; + padding: 10px; + text-transform: uppercase; + font-weight: bold; + font-size: 14px; + cursor: pointer; + color: $white; + background-color: $btn-background-color; + border-radius: 2px; + margin-bottom: 10px; + + &:hover { + background-color: $btn-background-color-hover; + color: $white; + } + } + } + + &-upload-main-bottom-buttons { + float: right; + margin-top: 30px; + + &-left, &-right { + display: inline-block; + padding: 10px; + text-transform: uppercase; + font-weight: bold; + font-size: 14px; + cursor: pointer; + color: $white; + background-color: $btn-background-color; + border-radius: 2px; + &:hover { + background-color: $btn-background-color-hover; + color: $white; + } + } + + &-left { + margin-right: 15px; + } + } + + &-upload-main-bottom-buttons-disabled, &-upload-main-bottom-buttons-disabled:hover { + cursor: default; + color: $btn-disabled-color; + background-color: $btn-disabled-background; + } + + &-upload-title { + font-weight: bold; + font-size: 20px; + } + + &-uploader-plugin { + min-height: unset; + width: 640px; + } + } + + input[type="file"] { + display: none; + } +} diff --git a/src/scss/components/_uploader.scss b/src/scss/components/_uploader.scss new file mode 100644 index 00000000..82639f12 --- /dev/null +++ b/src/scss/components/_uploader.scss @@ -0,0 +1,26 @@ +/** + * File upload plugin styles + */ +.gm-player-instance .gm-uploader-content { + .gm { + &-upload-icon { + display: inline-block; + height: 39px; + width: 58px; + background-repeat: no-repeat; + background-position: center center; + } + + &-upload-main-img { + background-image: url("../assets/images/ic_cloud_upload.svg"); + } + + &-upload-success-img { + background-image: url("../assets/images/ic_cloud_upload_congralutation.svg"); + } + + &-upload-error-img { + background-image: url("../assets/images/ic_cloud_upload_failed.svg"); + } + } +} diff --git a/src/scss/components/_widgetwindow.scss b/src/scss/components/_widgetwindow.scss new file mode 100644 index 00000000..01e5fe35 --- /dev/null +++ b/src/scss/components/_widgetwindow.scss @@ -0,0 +1,167 @@ +/** + * Widget window styles + */ +.gm-overlay { + font-family: Helvetica, sans-serif; + text-align: left; + position: absolute; + padding: 25px; + width: 500px; + left: calc(50% - 250px); + min-height: 400px; + top: 50px; + background-color: $modal-background-color; + color: $white; + border: none; + border-radius: 2px; + + @media only screen and (max-width : $breakpoint-mobile) { + max-width: 500px; + width: calc(80% - 40px) !important; + left: 10% !important; + } + + transition: top .4s; + -webkit-transition: top .25s ease-out; + + &.gm-hidden { + top: -1000px; + } + + .gm-wrap:after, .gm-horizontal:after { + display: table; + content: " "; + clear: both; + } + + .gm-col { + width: 50%; + float: left; + padding: 0 5px; + } + + .gm-title { + text-align: center; + margin-bottom: 30px; + } + + .gm-horizontal { + margin: 15px 0; + + label, input, button { + display: inline-block; + } + + label { + width: 50%; + } + } + + button { + color: $white; + background-color: $btn-background-color; + padding: 11px 25px; + + border-radius: 2px; + border: none; + transition: all 0.3s ease; + text-transform: uppercase; + outline: none; + outline-style: none; + + &:hover, &:focus { + cursor: pointer; + background-color: $btn-background-color-hover; + } + + &:disabled { + pointer-events: none; + transition: all 0.3s ease; + background: $btn-disabled-background; + color: $btn-disabled-color; + + &:hover{ + color: $btn-disabled-background-hover; + } + } + } + + label { + display: block; + margin-bottom: 3px; + } + + input, textarea { + // background-color: #444; + background: transparent; + color: $white; + + // border: 1px solid black; + border: none; + border-bottom: 1px solid $white; + + padding: 0 8px; + width: 100%; + outline: none; + + &:focus { + outline: none; + } + } + + select { + color: initial; + } + + input { + height: 32px; + } + + textarea { + padding: 8px; + } + + .gm-close-btn { + position: absolute; + top: 10px; + right: 10px; + width: 30px; + height: 30px; + background: url("../assets/images/ic-close-popup-default.svg") no-repeat top center; + + &:hover { + cursor: pointer; + background: url("../assets/images/ic-close-popup-hover.svg") no-repeat top center; + } + } +} + +.gm-disabled-widget-pop-up { + &::before { + position: relative; + float: left; + left: -220px; + top: -7px; + color: $white; + font-size: 15px; + font-family: Roboto, sans-serif; + pointer-events: none; + z-index: 5; + padding: 12px 8px 10px 8px; + width: 210px; + background-color: $modal-background-color; + vertical-align: middle; + content: "Not\00a0 currently\00a0supported"; + display: none; + } + + :hover::before { + display: block; + } +} + + +.gm-disabled-widget-icon { + opacity: 0.3; + pointer-events: none; +} diff --git a/src/scss/main.scss b/src/scss/main.scss new file mode 100644 index 00000000..3c9b4229 --- /dev/null +++ b/src/scss/main.scss @@ -0,0 +1,22 @@ +/** + * Main stylesheet. + * Only imports other styles. + */ + +@import "base/_variables"; +@import "base/_genymotion"; + +@import "components/_widgetwindow"; +@import "components/_fullscreen"; +@import "components/_toolbar"; +@import "components/_upload"; +@import "components/_battery"; +@import "components/_gps"; +@import "components/_screencast"; +@import "components/_imei"; +@import "components/_network"; +@import "components/_iothrottling"; +@import "components/_phone"; +@import "components/_uploader"; +@import "components/_clipboard"; +@import "components/_turn"; diff --git a/src/templates/bootstrap/dom.html b/src/templates/bootstrap/dom.html new file mode 100644 index 00000000..a934507c --- /dev/null +++ b/src/templates/bootstrap/dom.html @@ -0,0 +1,14 @@ +
+
+ +
+ +
+
+
    +
+
+
+
diff --git a/src/templates/bootstrap/script.js b/src/templates/bootstrap/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/bootstrap/style.css b/src/templates/bootstrap/style.css new file mode 100644 index 00000000..d71116a6 --- /dev/null +++ b/src/templates/bootstrap/style.css @@ -0,0 +1,36 @@ +.gm_bootstrap .gm-video { + background: transparent; + background-size: contain; + background-position: center; + width: 99% +} + +.gm_bootstrap { + background-color: black; +} + +.gm_bootstrap .gm-toolbars { + position: absolute; + width: 47px; + height: 100%; + top: 0; + right: 0; + margin: 0; +} + +.gm_bootstrap .gm-toolbar { + position: absolute; + top: 5%; + right: 0; + margin: 0; +} + +.gm_bootstrap li img { + margin: auto +} + +@media (min-width: 1200px) { + .gm_bootstrap li img { + width: 85%; + } +} diff --git a/src/templates/fullscreen/dom.html b/src/templates/fullscreen/dom.html new file mode 100644 index 00000000..a3a973e5 --- /dev/null +++ b/src/templates/fullscreen/dom.html @@ -0,0 +1,18 @@ + + + +
+
+ +
+
+
+
    +
+
+
+
+ + diff --git a/src/templates/fullscreen/script.js b/src/templates/fullscreen/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/fullscreen/style.css b/src/templates/fullscreen/style.css new file mode 100644 index 00000000..483334d4 --- /dev/null +++ b/src/templates/fullscreen/style.css @@ -0,0 +1,41 @@ +.gm-fullscreen { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 97%; + background-color: black; +} + +.gm-fullscreen .gm-fullscreen-message { + display: block; + padding: 0; + margin: 0; + height: 100%; + width: 100%; + background: white; + background-size: cover; +} + +.gm-video .hide { + display: none; +} + +.gm-fullscreen .gm-video { + background: transparent; + position: absolute; + top: 0; + left: 0; + width: 95%; + height: 100%; + padding: 0; +} + +.gm-toolbars { + position: absolute; + top: 0; + right: 0; +} diff --git a/src/templates/fullwindow/dom.html b/src/templates/fullwindow/dom.html new file mode 100644 index 00000000..97e06565 --- /dev/null +++ b/src/templates/fullwindow/dom.html @@ -0,0 +1,12 @@ +
+
+ +
+
+
    +
+
+
+ diff --git a/src/templates/fullwindow/script.js b/src/templates/fullwindow/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/fullwindow/style.css b/src/templates/fullwindow/style.css new file mode 100644 index 00000000..73715a8c --- /dev/null +++ b/src/templates/fullwindow/style.css @@ -0,0 +1,31 @@ +.gm_fullwindow { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 97%; + min-height: 560px; + background-color: black; +} + +.gm_fullwindow .gm-video { + background: transparent; + position: absolute; + top: 0; + left: 0; + width: calc(95% - 50px); + height: 100%; + padding: 0; +} + +.gm_fullwindow .toolbar { + position: absolute; + top: 10px; + right: 0; + padding-right: 10px; + margin: 0; + width: 50px; +} diff --git a/src/templates/player/dom.html b/src/templates/player/dom.html new file mode 100644 index 00000000..55c09229 --- /dev/null +++ b/src/templates/player/dom.html @@ -0,0 +1,15 @@ +
+
+
+ +
+
+
+
    +
+
+
+
+
diff --git a/src/templates/player/script.js b/src/templates/player/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/player/style.css b/src/templates/player/style.css new file mode 100644 index 00000000..cb4ab00f --- /dev/null +++ b/src/templates/player/style.css @@ -0,0 +1,23 @@ +.gm-template-player .gm-player { + position: relative; + border-collapse: separate; + border-radius: 3px; + padding: 40px 10px 40px 10px; + margin-left: auto; + margin-right: auto; + background-color: black; +} + +.gm-template-player .gm-video { + background: transparent url("../../assets/images/spinner-material.svg") no-repeat center; + width: 1024px; + height: 600px; + margin-right: 60px; +} + +.gm-template-player .gm-toolbars { + position: absolute; + top: 0; + right: 0; + width: 60px; +} diff --git a/src/templates/player_minimal/dom.html b/src/templates/player_minimal/dom.html new file mode 100644 index 00000000..ab2de35e --- /dev/null +++ b/src/templates/player_minimal/dom.html @@ -0,0 +1,12 @@ +
+
+ +
+
+
    +
+
+ +
diff --git a/src/templates/player_minimal/script.js b/src/templates/player_minimal/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/player_minimal/style.css b/src/templates/player_minimal/style.css new file mode 100644 index 00000000..572c0fae --- /dev/null +++ b/src/templates/player_minimal/style.css @@ -0,0 +1,41 @@ +.gm-template-player_minimal { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 100%; + min-height: 560px; + background-color: black; +} + +.gm-template-player_minimal .gm-video { + position: fixed; + top: 0; + left: 0; + width: calc(100vw - 57px); + height: 100vh; + padding: 0; + margin: 0; + background: transparent url("../../assets/images/loader.svg") center no-repeat; + background-size: 40%; +} + +.gm-template-player_minimal .gm-toolbar { + position: absolute; + top: 10px; + right: 0; + padding-right: 5px; + margin: 0; + width: 50px; +} + +.gm-template-player_minimal .gm-version-number { + position: fixed; + right: 5px; + bottom: 5px; + z-index: 2; + color: white; +} diff --git a/src/templates/player_no_toolbar/dom.html b/src/templates/player_no_toolbar/dom.html new file mode 100644 index 00000000..a1935a59 --- /dev/null +++ b/src/templates/player_no_toolbar/dom.html @@ -0,0 +1,7 @@ +
+
+ +
+
\ No newline at end of file diff --git a/src/templates/player_no_toolbar/script.js b/src/templates/player_no_toolbar/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/player_no_toolbar/style.css b/src/templates/player_no_toolbar/style.css new file mode 100644 index 00000000..8e01842d --- /dev/null +++ b/src/templates/player_no_toolbar/style.css @@ -0,0 +1,23 @@ +.gm-template-player_no_toolbar { + position: absolute; + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 100%; + min-height: 560px; + background-color: black; +} + +.gm-template-player_no_toolbar .gm-video { + position: fixed; + top: 0; + left: 0; + width: calc(95% - 50px); + height: 100%; + padding: 0; + background: transparent url("../../assets/images/loader.svg") center no-repeat; + background-size: 40%; +} diff --git a/src/templates/player_partial/dom.html b/src/templates/player_partial/dom.html new file mode 100644 index 00000000..ab2de35e --- /dev/null +++ b/src/templates/player_partial/dom.html @@ -0,0 +1,12 @@ +
+
+ +
+
+
    +
+
+ +
diff --git a/src/templates/player_partial/script.js b/src/templates/player_partial/script.js new file mode 100644 index 00000000..e69de29b diff --git a/src/templates/player_partial/style.css b/src/templates/player_partial/style.css new file mode 100644 index 00000000..c7df440b --- /dev/null +++ b/src/templates/player_partial/style.css @@ -0,0 +1,74 @@ +.gm-template-player_partial { + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + min-height: 800px; + height: 100%; + background-color: black; +} + +.gm-template-player_partial .gm-wrapper { + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 100%; +} + +.gm-template-player_partial .gm-video-wrapper { + position: relative; + top: 0; + left: 0; + margin: 0; + padding: 0; + display: block; + width: 100%; + height: 100%; +} + +.gm-template-player_partial .gm-video { + position: relative; + top: 0; + left: 0; + width: calc(95% - 50px); + height: 100%; + padding: 0; + background: transparent url("../../assets/images/loader.svg") center no-repeat; + background-size: 40%; +} + +.gm-template-player_partial .gm-toolbar { + position: absolute; + top: 10px; + right: 0; + padding-right: 5px; + margin: 0; + width: 50px; +} + +.gm-template-player_partial .gm-version-number { + position: fixed; + right: 5px; + bottom: 5px; + z-index: 2; + color: white; +} + +@media only screen and (max-width: 650px) { + .gm-overlay { + left: auto; + width: calc(100vw - 100px); + right: 55px; + min-height: 0; + } +} + +@media only screen and (min-width: 475px) { + .gm-click-to-unmute { + left: calc(50% - 190px) !important; + } +} diff --git a/src/worker/FileUploaderWorker.js b/src/worker/FileUploaderWorker.js new file mode 100644 index 00000000..c23e66d2 --- /dev/null +++ b/src/worker/FileUploaderWorker.js @@ -0,0 +1,146 @@ +'use strict'; + +module.exports = function() { + const self = this; + this.socket = null; + this.token = null; + this.hasError = false; + this.isUploading = false; + this.uploadedSize = 0; + this.file = null; + this.address = null; + this.MEGABYTE = 1000 * 1000; + + self.getChunkSize = function(fileSize) { + /* + * The minimum upload size of chunk is 5MB. + * If we are uploading a "big" file, we are uploading 10% by 10%. + * The maximum chunk size is 75MB to avoid "out of memory" errors on PaaS. + */ + const minimum = 5 * self.MEGABYTE; + const maximum = 75 * self.MEGABYTE; + return Math.max(minimum, Math.min(0.1 * fileSize, maximum)); + }; + + self.onOpen = function() { + const tokenRequest = { + type: 'token', + token: self.token, + }; + self.socket.send(JSON.stringify(tokenRequest)); + }; + + self.onClose = function() { + setTimeout(function() { + self.connect(self.address); + }, 1000); + }; + + self.connect = function(address) { + self.socket = new WebSocket(address); + self.socket.binaryType = 'arraybuffer'; + self.socket.onopen = self.onOpen; + self.socket.onerror = self.onFailure; + self.socket.onmessage = self.onSocketMsg; + self.socket.onclose = self.onClose; + }; + + self.onFailure = function() { + const msg = { + type: 'FILE_UPLOAD', + code: 'FAIL', + }; + self.isUploading = false; + postMessage(msg); + }; + + self.uploadData = function(event) { + if (self.hasError === false) { + self.socket.send(event.target.result, {binary: true}); + } + }; + + self.onSocketMsg = function(evt) { + const msg = JSON.parse(evt.data); + + if (msg.type === 'FILE_UPLOAD') { + switch (msg.code) { + case 'NEXT': { + if (self.uploadedSize < self.file.size) { + const chunkLength = self.getChunkSize(self.file.size); + let blob; + const reader = new FileReader(); + reader.onload = self.uploadData; + reader.onabort = self.onFailure; + reader.onerror = self.onFailure; + + if (self.file.size - self.uploadedSize > chunkLength) { + blob = self.file.slice(self.uploadedSize, self.uploadedSize + chunkLength); + self.uploadedSize += chunkLength; + } else { + blob = self.file.slice(self.uploadedSize); + self.uploadedSize = self.file.size; + } + reader.readAsArrayBuffer(blob); + } else { + const status = { + type: 'FILE_UPLOAD', + 'done': true, + }; + self.socket.send(JSON.stringify(status)); + self.isUploading = false; + } + break; + } + case 'PROGRESS': + postMessage(msg); + break; + case 'SUCCESS': + postMessage(msg); + break; + case 'FAIL': + self.hasError = true; + postMessage(msg); + break; + default: + break; + } + } + }; + + self.onmessage = function(event) { + const msg = event.data; + + switch (msg.type) { + case 'address': + self.token = msg.token; + self.address = msg.fileUploadAddress; + self.connect(self.address); + break; + case 'close': + self.isUploading = false; + if (self.socket) { + self.socket.onclose = null; + self.socket.close(); + } + break; + case 'upload': + if (!self.isUploading) { + self.file = msg.file; + self.isUploading = true; + self.hasError = false; + self.uploadedSize = 0; + + const data = { + type: 'FILE_UPLOAD', + 'name': self.file.name, + 'size': self.file.size, + }; + self.socket.send(JSON.stringify(data)); + } + break; + default: + break; + } + }; +}; diff --git a/tests/config.js b/tests/config.js new file mode 100644 index 00000000..6529c4ab --- /dev/null +++ b/tests/config.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + clearMocks: true, + reporters: ['default', 'jest-junit'], + testMatch: [ + '**/?(*.)+(test).js' + ] +}; diff --git a/tests/mocks/GenymotionInstance.js b/tests/mocks/GenymotionInstance.js new file mode 100644 index 00000000..6fbc748e --- /dev/null +++ b/tests/mocks/GenymotionInstance.js @@ -0,0 +1,68 @@ +'use strict'; + +const Original = require('../../src/GenymotionInstance'); + +module.exports = class GenymotionInstance extends Original { + constructor(options) { + document.body.innerHTML = ` +
+
+
+ +
+
+
+
    +
+
+
+
+
`; + + window.adapter = { + browserDetails: { + browser: 'test' + } + }; + + navigator.mediaDevices = { + getUserMedia: jest.fn().mockReturnValue(Promise.resolve(null)) + }; + + super(document.body, options || {}); + this.outgoingMessages = []; + } + + /** + * Send event to the instance through the Websocket connection. + * + * @param {Object} event Event to send. + */ + sendEvent(event) { + this.outgoingMessages.push(event); + } + + /** + * Add a local stream and send it through SDP renegotiation. + */ + addLocalStream() { + + } + + /** + * Remove a local stream and stop sending it through SDP renegotiation. + */ + removeLocalStream() { + + } + + /** + * Reconfigure & setup the peer-to-peer connection (SDP). + * Can be used anytime to renegotiate the SDP if necessary. + */ + renegotiateWebRTCConnection() { + + } +}; diff --git a/tests/unit/battery.test.js b/tests/unit/battery.test.js new file mode 100644 index 00000000..04b50d14 --- /dev/null +++ b/tests/unit/battery.test.js @@ -0,0 +1,181 @@ +'use strict'; + +const Battery = require('../../src/plugins/Battery'); +const Instance = require('../mocks/GenymotionInstance'); + +let battery; +let instance; +let plugin; + +describe('Battery Plugin', () => { + beforeEach(() => { + instance = new Instance(); + battery = new Battery(instance, {}); + plugin = document.getElementsByClassName('gm-battery-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Battery).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + battery = new Battery(instance, { + BATTERY_TITLE: 'TEST BATTERY PLUGIN TITLE', + BATTERY_CHARGE_LEVEL: 'TEST BATTERY PLUGIN CHARGE LEVEL', + BATTERY_CHARGE_STATE: 'TEST BATTERY PLUGIN CHARGE STATE' + }); + plugin = document.getElementsByClassName('gm-battery-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-battery-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-battery-button')).toHaveLength(1); + }); + + test('has default values', () => { + expect(battery.chargingInput.checked).toBeTruthy(); + expect(Number(battery.chargeSlider.value)).toBe(50); + expect(Number(battery.chargeInput.value)).toBe(50); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BATTERY PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BATTERY PLUGIN CHARGE LEVEL')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BATTERY PLUGIN CHARGE STATE')); + }); + }); + + describe('incoming events', () => { + let statusMock; + let levelMock; + + beforeEach(() => { + statusMock = jest.spyOn(battery, 'updateUIBatteryChargingState'); + levelMock = jest.spyOn(battery, 'onBatteryLevelChange'); + }); + + afterEach(() => { + statusMock.mockRestore(); + levelMock.mockRestore(); + }); + + test('BATTERY_STATUS', () => { + instance.emit('BATTERY_STATUS', 'jean-michel'); // Invalid value (=== charging) + expect(battery.chargingInput.checked).toBeTruthy(); + + instance.emit('BATTERY_STATUS', 'false'); // Discharging + expect(battery.chargingInput.checked).toBeFalsy(); + + instance.emit('BATTERY_STATUS', 'true'); // Charging + expect(battery.chargingInput.checked).toBeTruthy(); + }); + + test('BATTERY_LEVEL', () => { + instance.emit('BATTERY_LEVEL', 'jean-michel'); // Invalid value + expect(Number(battery.chargeSlider.value)).toBe(50); + expect(Number(battery.chargeInput.value)).toBe(50); + + instance.emit('BATTERY_LEVEL', '69'); // Nice + expect(Number(battery.chargeSlider.value)).toBe(69); + expect(Number(battery.chargeInput.value)).toBe(69); + + instance.emit('BATTERY_LEVEL', '666'); // Overflow + expect(Number(battery.chargeSlider.value)).toBe(100); + expect(Number(battery.chargeInput.value)).toBe(100); + + instance.emit('BATTERY_LEVEL', '-420'); // Underflow + expect(Number(battery.chargeSlider.value)).toBe(0); + expect(Number(battery.chargeInput.value)).toBe(0); + }); + + test('battery', () => { + instance.emit('battery', 'jean-michel'); // Bad format + expect(levelMock).not.toHaveBeenCalled(); + expect(statusMock).not.toHaveBeenCalled(); + + instance.emit('battery', 'state mode badstate badvalue'); // Bad state and value + expect(levelMock).toHaveBeenLastCalledWith('badvalue'); + expect(statusMock).toHaveBeenLastCalledWith(true); + expect(battery.chargingInput.checked).toBeTruthy(); + expect(Number(battery.chargeSlider.value)).toBe(50); + expect(Number(battery.chargeInput.value)).toBe(50); + + instance.emit('battery', 'state mode badstate 69'); // Bad state + expect(levelMock).toHaveBeenLastCalledWith('69'); + expect(statusMock).toHaveBeenLastCalledWith(true); + expect(battery.chargingInput.checked).toBeTruthy(); + expect(Number(battery.chargeSlider.value)).toBe(69); + expect(Number(battery.chargeInput.value)).toBe(69); + + instance.emit('battery', 'state mode discharging badlevel'); // Bad value + expect(levelMock).toHaveBeenLastCalledWith('badlevel'); + expect(statusMock).toHaveBeenLastCalledWith(false); + expect(battery.chargingInput.checked).toBeFalsy(); + expect(Number(battery.chargeSlider.value)).toBe(69); // Previous value + expect(Number(battery.chargeInput.value)).toBe(69); // Previous value + + instance.emit('battery', 'state mode charging 42'); // Good state and value + expect(levelMock).toHaveBeenLastCalledWith('42'); + expect(statusMock).toHaveBeenLastCalledWith(true); + expect(battery.chargingInput.checked).toBeTruthy(); + expect(Number(battery.chargeSlider.value)).toBe(42); + expect(Number(battery.chargeInput.value)).toBe(42); + }); + }); + + describe('outgoing events', () => { + let sendEventSpy; + + beforeEach(() => { + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + }); + + afterEach(() => { + sendEventSpy.mockRestore(); + }); + + test('battery level input', () => { + battery.chargeInput.value = 69; // nice + battery.chargeInput.dispatchEvent(new Event('input')); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'battery', messages: [ + 'set state level 69', + 'set state status charging', + ], + }); + }); + + test('battery level slider', () => { + battery.chargeSlider.value = 42; + battery.chargeSlider.dispatchEvent(new Event('change')); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'battery', messages: [ + 'set state level 42', + 'set state status charging', + ], + }); + }); + + test('battery state', () => { + battery.chargingInput.click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'battery', messages: [ + 'set state level 50', + 'set state status discharging', + ], + }); + }); + }); +}); diff --git a/tests/unit/buttonsevent.test.js b/tests/unit/buttonsevent.test.js new file mode 100644 index 00000000..1bf8f89e --- /dev/null +++ b/tests/unit/buttonsevent.test.js @@ -0,0 +1,236 @@ +'use strict'; + +const ButtonsEvents = require('../../src/plugins/ButtonsEvents'); +const Instance = require('../mocks/GenymotionInstance'); + +let instance; + +describe('ButtonsEvents Plugin', () => { + beforeEach(() => { + instance = new Instance({ + rotation: true, + volume: true, + navbar: true, + power: true + }); + new ButtonsEvents(instance, {}, false); + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof ButtonsEvents).toBe('function'); + }); + }); + + describe('UI', () => { + test('is initialized properly at construct', () => { + instance = new Instance({ + rotation: true, + volume: true, + navbar: true, + power: true + }); + new ButtonsEvents(instance, {}, false); + + // Toolbar buttons + expect(document.getElementsByClassName('gm-rotate')).toHaveLength(1); + expect(document.getElementsByClassName('gm-sound-down')).toHaveLength(1); + expect(document.getElementsByClassName('gm-sound-up')).toHaveLength(1); + expect(document.getElementsByClassName('gm-recent')).toHaveLength(1); + expect(document.getElementsByClassName('gm-home')).toHaveLength(1); + expect(document.getElementsByClassName('gm-back')).toHaveLength(1); + expect(document.getElementsByClassName('gm-power')).toHaveLength(1); + }); + + test('handles instance options', () => { + instance = new Instance({ + rotation: false, + volume: null, + }); + new ButtonsEvents(instance, {}, false); + + // Toolbar buttons + expect(document.getElementsByClassName('gm-rotate')).toHaveLength(0); + expect(document.getElementsByClassName('gm-sound-down')).toHaveLength(0); + expect(document.getElementsByClassName('gm-sound-up')).toHaveLength(0); + expect(document.getElementsByClassName('gm-recent')).toHaveLength(0); + expect(document.getElementsByClassName('gm-home')).toHaveLength(0); + expect(document.getElementsByClassName('gm-back')).toHaveLength(0); + expect(document.getElementsByClassName('gm-power')).toHaveLength(0); + }); + + test('has translations', () => { + instance = new Instance({ + rotation: true, + volume: true, + navbar: true, + power: true + }); + new ButtonsEvents(instance, { + BUTTONS_ROTATE: 'TEST BUTTONS EVENTS ROTATE BUTTON', + BUTTONS_SOUND_DOWN: 'TEST BUTTONS EVENTS SOUND_DOWN BUTTON', + BUTTONS_SOUND_UP: 'TEST BUTTONS EVENTS SOUND_UP BUTTON', + BUTTONS_RECENT_APPS: 'TEST BUTTONS EVENTS RECENT_APPS BUTTON', + BUTTONS_HOME: 'TEST BUTTONS EVENTS HOME BUTTON', + BUTTONS_BACK: 'TEST BUTTONS EVENTS BACK BUTTON', + BUTTONS_POWER: 'TEST BUTTONS EVENTS POWER BUTTON' + }, false); + + // Toolbar buttons + const plugin = document.body; + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS ROTATE BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS SOUND_DOWN BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS SOUND_UP BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS RECENT_APPS BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS HOME BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS BACK BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST BUTTONS EVENTS POWER BUTTON')); + }); + }); + + describe('outgoing events', () => { + let sendEventSpy; + + beforeEach(() => { + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + }); + + afterEach(() => { + sendEventSpy.mockRestore(); + }); + + test('rotation', () => { + const button = document.getElementsByClassName('gm-rotate')[0]; + + button.dispatchEvent(new Event('mousedown')); + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'ROTATE' + }); + }); + + test('volume up', () => { + const button = document.getElementsByClassName('gm-sound-up')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000072'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000072'), keychar: '0\n' + }); + }); + + test('volume down', () => { + const button = document.getElementsByClassName('gm-sound-down')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000070'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000070'), keychar: '0\n' + }); + }); + + test('recent apps', () => { + const button = document.getElementsByClassName('gm-recent')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x010000be'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x010000be'), keychar: '0\n' + }); + }); + + test('home - default', () => { + const button = document.getElementsByClassName('gm-home')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000010'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000010'), keychar: '0\n' + }); + }); + + test('home - translated', () => { + instance = new Instance({ + navbar: true + }); + new ButtonsEvents(instance, {}, true); + const button = document.getElementsByClassName('gm-home')[0]; + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000022'), keychar: '' + }); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000005'), keychar: '' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(4); + expect(instance.outgoingMessages[2]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000005'), keychar: '' + }); + expect(instance.outgoingMessages[3]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000022'), keychar: '' + }); + }); + + test('back', () => { + const button = document.getElementsByClassName('gm-back')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x01000061'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x01000061'), keychar: '0\n' + }); + }); + + test('power', () => { + const button = document.getElementsByClassName('gm-power')[0]; + + button.dispatchEvent(new Event('mousedown')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + type: 'KEYBOARD_PRESS', keycode: parseInt('0x0100010b'), keychar: '0\n' + }); + + button.dispatchEvent(new Event('mouseup')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + type: 'KEYBOARD_RELEASE', keycode: parseInt('0x0100010b'), keychar: '0\n' + }); + }); + }); +}); diff --git a/tests/unit/camera.test.js b/tests/unit/camera.test.js new file mode 100644 index 00000000..af3c10ba --- /dev/null +++ b/tests/unit/camera.test.js @@ -0,0 +1,105 @@ +'use strict'; + +jest.mock('loglevel'); + +const Camera = require('../../src/plugins/Camera'); +const Instance = require('../mocks/GenymotionInstance'); + +let camera; +let instance; + +describe('Camera Plugin', () => { + beforeEach(() => { + instance = new Instance({ + rotation: true, + volume: true, + navbar: true, + power: true + }); + camera = new Camera(instance, false); + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Camera).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new Camera(instance, { + CAMERA_TITLE: 'TEST CAMERA PLUGIN BUTTON TITLE' + }); + }); + + test('is initialized properly at construct', () => { + // Toolbar button + expect(document.getElementsByClassName('gm-camera-button')).toHaveLength(1); + }); + + test('has translations', () => { + const plugin = document.body; + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST CAMERA PLUGIN BUTTON TITLE')); + }); + }); + + describe('camera', () => { + let startSpy; + let stopSpy; + + beforeEach(() => { + startSpy = jest.spyOn(camera, 'startStreaming'); + stopSpy = jest.spyOn(camera, 'stopStreaming'); + + camera.onVideoStreamError = jest.fn(); + instance.addLocalStream = jest.fn(); + }); + + afterEach(() => { + startSpy.mockRestore(); + stopSpy.mockRestore(); + navigator.mediaDevices.getUserMedia.mockRestore(); + camera.onVideoStreamError.mockRestore(); + instance.addLocalStream.mockRestore(); + }); + + it('can be toggled on', (done) => { + const plugin = document.getElementsByClassName('gm-camera-button')[0]; + + instance.addLocalStream.mockImplementationOnce(() => { + expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledTimes(1); + expect(startSpy).toHaveBeenCalledTimes(1); + expect(stopSpy).toHaveBeenCalledTimes(0); + expect(camera.onVideoStreamError).toHaveBeenCalledTimes(0); + done(); + }); + + plugin.click(); + }); + + it('can be toggled off', () => { + const plugin = document.getElementsByClassName('gm-camera-button')[0]; + + camera.streaming = true; + plugin.click(); + expect(startSpy).toHaveBeenCalledTimes(0); + expect(stopSpy).toHaveBeenCalledTimes(1); + }); + + it('handle mediaDevices errors', (done) => { + const plugin = document.getElementsByClassName('gm-camera-button')[0]; + + camera.onVideoStreamError.mockImplementationOnce(() => { + expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledTimes(1); + expect(startSpy).toHaveBeenCalledTimes(1); + expect(stopSpy).toHaveBeenCalledTimes(0); + expect(instance.addLocalStream).toHaveBeenCalledTimes(0); + expect(camera.onVideoStreamError).toHaveBeenCalledTimes(1); + done(); + }); + navigator.mediaDevices.getUserMedia.mockReturnValueOnce(Promise.reject(null)); + plugin.click(); + }); + }); +}); diff --git a/tests/unit/clipboard.test.js b/tests/unit/clipboard.test.js new file mode 100644 index 00000000..657d9690 --- /dev/null +++ b/tests/unit/clipboard.test.js @@ -0,0 +1,110 @@ +'use strict'; + +jest.mock('loglevel'); + +const Clipboard = require('../../src/plugins/Clipboard'); +const Instance = require('../mocks/GenymotionInstance'); + +let clipboard; +let instance; +let plugin; + +describe('Clipboard Plugin', () => { + beforeEach(() => { + instance = new Instance(); + clipboard = new Clipboard(instance, {}); + plugin = document.getElementsByClassName('gm-clipboard-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Clipboard).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + clipboard = new Clipboard(instance, { + CLIPBOARD_TITLE: 'TEST CLIPBOARD PLUGIN TITLE', + CLIPBOARD_COPIED: 'TEST CLIPBOARD COPIED TO CLIPBOARD' + }); + plugin = document.getElementsByClassName('gm-clipboard-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-clipboard-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-clipboard-button')).toHaveLength(1); + }); + + test('has default values', () => { + expect(clipboard.clipboardInput.value).toBe(''); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST CLIPBOARD PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST CLIPBOARD COPIED TO CLIPBOARD')); + }); + + test('reflects internal clipboard state when displayed', () => { + const button = document.getElementsByClassName('gm-clipboard-button')[0]; + + clipboard.clipboard = 'test value'; + button.click(); + expect(clipboard.clipboardInput.value).toBe('test value'); + }); + }); + + describe('incoming events', () => { + test('CLIPBOARD', () => { + instance.emit('CLIPBOARD', 'test value'); + expect(clipboard.clipboard).toBe('test value'); + }); + + test('framework', () => { + instance.emit('framework', 'clipboard too many arguments'); + expect(clipboard.clipboard).toBe(''); + + instance.emit('framework', 'clipboard missingparam'); + expect(clipboard.clipboard).toBe(''); + + instance.emit('framework', 'unrelevant channel'); + expect(clipboard.clipboard).toBe(''); + + instance.emit('framework', 'clipboard from_android badvalue'); // Bad value + expect(clipboard.clipboard).toBe(''); + + instance.emit('framework', 'clipboard from_android dGVzdCB2YWx1ZQ=='); + expect(clipboard.clipboard).toBe('test value'); + }); + }); + + describe('outgoing events', () => { + let sendEventSpy; + + beforeEach(() => { + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + }); + + afterEach(() => { + sendEventSpy.mockRestore(); + }); + + test('clipboard value', () => { + const button = document.getElementsByClassName('gm-clipboard-button')[0]; + + clipboard.clipboard = 'test value'; + button.click(); // Open + button.click(); // Close + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'framework', messages: [ + 'set_device_clipboard dGVzdCB2YWx1ZQ==', + ], + }); + }); + }); +}); diff --git a/tests/unit/fileupload.test.js b/tests/unit/fileupload.test.js new file mode 100644 index 00000000..95f313eb --- /dev/null +++ b/tests/unit/fileupload.test.js @@ -0,0 +1,293 @@ +'use strict'; + +const FileUpload = require('../../src/plugins/FileUpload'); +const Instance = require('../mocks/GenymotionInstance'); + +let uploader; +let instance; +let plugin; + +describe('FileUpload Plugin', () => { + beforeEach(() => { + instance = new Instance(); + uploader = new FileUpload(instance, { + UPLOADER_INSTALLING: 'TEST UPLOADER PLUGIN INSTALLING...' + }); + plugin = document.getElementsByClassName('gm-uploader-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof FileUpload).toBe('function'); + }); + + it('should attach ondragover, ondragenter, and ondragleave to root', () => { + expect(typeof instance.videoWrapper.ondragover).toBe('function'); + expect(typeof instance.videoWrapper.ondragleave).toBe('function'); + expect(typeof instance.videoWrapper.ondragenter).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + uploader = new FileUpload(instance, { + UPLOADER_TITLE: 'TEST UPLOADER PLUGIN TITLE', + UPLOADER_HOME_TITLE: 'TEST UPLOADER PLUGIN HOME TITLE', + UPLOADER_DISCLAIMER: 'TEST UPLOADER PLUGIN DISCLAIMER', + UPLOADER_INPROGRESS: 'TEST UPLOADER PLUGIN IN PROGRESS', + UPLOADER_SUCCESS: 'TEST UPLOADER PLUGIN SUCCESS', + UPLOADER_FAILURE: 'TEST UPLOADER PLUGIN FAILURE' + + }); + plugin = document.getElementsByClassName('gm-uploader-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-uploader-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-uploader-button')).toHaveLength(1); + }); + + test('has translations', () => { + uploader.displayStep('homeScreen'); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST UPLOADER PLUGIN HOME TITLE')); + uploader.displayStep('disclaimerScreen'); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST UPLOADER PLUGIN DISCLAIMER')); + uploader.displayStep('uploadScreen'); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST UPLOADER PLUGIN IN PROGRESS')); + uploader.displayStep('successScreen'); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST UPLOADER PLUGIN SUCCESS')); + uploader.displayStep('errorScreen'); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST UPLOADER PLUGIN FAILURE')); + }); + + test('allows access to disclaimer on right conditions', () => { + uploader.displayStep('homeScreen'); + const displayStep = jest.spyOn(uploader, 'displayStep'); + + // Not allowed by default + uploader.installButton.click(); + expect(displayStep).toHaveBeenCalledTimes(0); + + // Already installed + uploader.opengappsInstalled = true; + uploader.capabilityAvailable = true; + uploader.installButton.click(); + expect(displayStep).toHaveBeenCalledTimes(0); + + // Not available + uploader.opengappsInstalled = false; + uploader.capabilityAvailable = false; + uploader.installButton.click(); + expect(displayStep).toHaveBeenCalledTimes(0); + + // Available + uploader.opengappsInstalled = false; + uploader.capabilityAvailable = true; + uploader.installButton.click(); + expect(displayStep).toHaveBeenNthCalledWith(1, 'disclaimerScreen'); + }); + + test('sets proper step on toggle', () => { + uploader.displayStep('homeScreen'); + uploader.toggleWidget(); + uploader.toggleWidget(); + expect(uploader.currentStep).toEqual('homeScreen'); + uploader.displayStep('disclaimerScreen'); + uploader.toggleWidget(); + uploader.toggleWidget(); + expect(uploader.currentStep).toEqual('disclaimerScreen'); + uploader.displayStep('uploadScreen'); + uploader.toggleWidget(); + uploader.toggleWidget(); + expect(uploader.currentStep).toEqual('uploadScreen'); + uploader.displayStep('successScreen'); + uploader.toggleWidget(); + uploader.toggleWidget(); + expect(uploader.currentStep).toEqual('homeScreen'); + uploader.displayStep('errorScreen'); + uploader.toggleWidget(); + uploader.toggleWidget(); + expect(uploader.currentStep).toEqual('homeScreen'); + }); + + test('Dont force show on success', () => { + uploader.flashing = false; + uploader.displayStep('uploadScreen'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'success'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + }); + + test('dont force show on error', () => { + uploader.flashing = false; + uploader.displayStep('uploadScreen'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'install_error'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + }); + + test('force show on opengapps success', () => { + uploader.flashing = true; + uploader.displayStep('uploadScreen'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'success'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(0); + }); + + test('force show on opengapps error', () => { + uploader.flashing = true; + uploader.displayStep('uploadScreen'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(1); + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'install_error'); + expect(document.getElementsByClassName('gm-hidden').length).toEqual(0); + }); + }); + + describe('incoming events', () => { + describe('SYSTEM_PATCHER_STATUS', () => { + test('downloading', () => { + uploader.displayStep('uploadScreen'); + const textProgress = document.getElementsByClassName('gm-upload-in-progress-txt-percent')[0]; + + instance.emit('SYSTEM_PATCHER_STATUS', 'downloading something'); // Not enough parameters + expect(textProgress.innerHTML).toBe(''); + + instance.emit('SYSTEM_PATCHER_STATUS', 'downloading 69 100'); + expect(textProgress.innerHTML).toBe('69%'); // Nice + }); + + test('installing', () => { + uploader.displayStep('uploadScreen'); + const textProgress = document.getElementsByClassName('gm-upload-in-progress-txt-progress')[0]; + + instance.emit('SYSTEM_PATCHER_STATUS', 'installing'); + expect(textProgress.innerHTML).toBe('TEST UPLOADER PLUGIN INSTALLING...'); + }); + + test('ready', () => { + const updateOpenGAppsStatus = jest.spyOn(uploader, 'updateOpenGAppsStatus'); + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + instance.emit('SYSTEM_PATCHER_STATUS', 'ready'); // Not enough parameters + expect(updateOpenGAppsStatus).toHaveBeenCalledTimes(0); + + instance.emit('SYSTEM_PATCHER_STATUS', 'ready something'); + expect(updateOpenGAppsStatus).toHaveBeenCalledTimes(0); + + // Asumes uploader.flashing = false, default + instance.emit('SYSTEM_PATCHER_STATUS', 'ready opengapps'); + expect(updateOpenGAppsStatus).toHaveBeenCalledTimes(1); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + + uploader.flashing = true; + instance.emit('SYSTEM_PATCHER_STATUS', 'ready opengapps'); + expect(updateOpenGAppsStatus).toHaveBeenCalledTimes(2); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'systempatcher', messages: [ + 'notify last_result' + ] + }); + }); + }); + + test('SYSTEM_PATCHER_LAST_RESULT', () => { + const displayStep = jest.spyOn(uploader, 'displayStep'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'success'); + expect(displayStep).toHaveBeenNthCalledWith(1, 'successScreen'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'unavailable'); + expect(displayStep).toHaveBeenNthCalledWith(2, 'errorScreen'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'network_error'); + expect(displayStep).toHaveBeenNthCalledWith(3, 'errorScreen'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'corrupted_archive'); + expect(displayStep).toHaveBeenNthCalledWith(4, 'errorScreen'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'install_error'); + expect(displayStep).toHaveBeenNthCalledWith(5, 'errorScreen'); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', 'something'); + expect(displayStep).toHaveBeenCalledTimes(5); + + instance.emit('SYSTEM_PATCHER_LAST_RESULT', ''); + expect(displayStep).toHaveBeenCalledTimes(5); + }); + + test('systempatcher', () => { + const onSystemPatcherStatusEvent = jest.spyOn(uploader, 'onSystemPatcherStatusEvent'); + const onSystemPatcherLastResultEvent = jest.spyOn(uploader, 'onSystemPatcherLastResultEvent'); + + instance.emit('systempatcher', 'status some various params'); + expect(onSystemPatcherStatusEvent).toHaveBeenNthCalledWith(1, 'some various params'); + expect(onSystemPatcherLastResultEvent).toHaveBeenCalledTimes(0); + + instance.emit('systempatcher', 'last_result some various params'); + expect(onSystemPatcherStatusEvent).toHaveBeenCalledTimes(1); + expect(onSystemPatcherLastResultEvent).toHaveBeenCalledTimes(1, 'some various params'); + + instance.emit('systempatcher', 'unrelevant some various params'); + expect(onSystemPatcherStatusEvent).toHaveBeenCalledTimes(1); + expect(onSystemPatcherLastResultEvent).toHaveBeenCalledTimes(1); + }); + }); + + describe('outgoing events', () => { + let sendEventSpy; + + beforeEach(() => { + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + }); + + afterEach(() => { + sendEventSpy.mockRestore(); + }); + + test('flash opengapps', () => { + uploader.displayStep('disclaimerScreen'); + const button = document.getElementsByClassName('gm-upload-main-bottom-buttons-right')[0]; + + button.click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'systempatcher', messages: [ + 'install opengapps' + ] + }); + }); + + test('cancel flash opengapps', () => { + uploader.displayStep('uploadScreen'); + const button = document.getElementsByClassName('gm-upload-main-bottom-buttons-right')[0]; + + button.click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'systempatcher', messages: [ + 'cancel' + ] + }); + }); + + test('reboot instance', () => { + uploader.displayStep('successScreen'); + const button = document.getElementsByClassName('gm-upload-success-button')[0]; + + button.click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'systempatcher', messages: [ + 'reboot' + ] + }); + }); + }); +}); diff --git a/tests/unit/fullscreen.test.js b/tests/unit/fullscreen.test.js new file mode 100644 index 00000000..960257a3 --- /dev/null +++ b/tests/unit/fullscreen.test.js @@ -0,0 +1,25 @@ + +'use strict'; + +const FullScreen = require('../../src/plugins/Fullscreen'); +const Instance = require('../mocks/GenymotionInstance'); + +describe('FullScreen Plugin', () => { + beforeEach(() => { + const instance = new Instance(); + new FullScreen(instance); + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof FullScreen).toBe('function'); + }); + }); + + describe('UI', () => { + test('is initialized properly at construct', () => { + // Toolbar button + expect(document.getElementsByClassName('gm-fullscreen-button')).toHaveLength(1); + }); + }); +}); diff --git a/tests/unit/gps.test.js b/tests/unit/gps.test.js new file mode 100644 index 00000000..251b8fd6 --- /dev/null +++ b/tests/unit/gps.test.js @@ -0,0 +1,352 @@ +'use strict'; + +jest.mock('loglevel'); + +const GPS = require('../../src/plugins/GPS'); +const Instance = require('../mocks/GenymotionInstance'); + +let gps; +let instance; +let controls; +let map; + +describe('GPS Plugin', () => { + beforeEach(() => { + instance = new Instance(); + gps = new GPS(instance, {}, true); + controls = document.getElementsByClassName('gm-gps-controls')[0]; + map = document.getElementsByClassName('gm-gps-mapview')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof GPS).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + gps = new GPS(instance, { + GPS_TITLE: 'TEST GPS PLUGIN TITLE', + GPS_CANCEL: 'TEST GPS PLUGIN CANCEL', + GPS_CAPTURE: 'TEST GPS PLUGIN CAPTURE', + GPS_LATITUDE: 'TEST GPS PLUGIN LATITUDE', + GPS_LONGITUDE: 'TEST GPS PLUGIN LONGITUDE', + GPS_ALTITUDE: 'TEST GPS PLUGIN ALTITUDE', + GPS_ACCURACY: 'TEST GPS PLUGIN ACCURACY', + GPS_BEARING: 'TEST GPS PLUGIN BEARING', + GPS_SPEED: 'TEST GPS PLUGIN SPEED', + GPS_MAP: 'TEST GPS PLUGIN MAP', + GPS_GEOLOC: 'TEST GPS PLUGIN GEOLOC', + GPS_SUBMIT: 'TEST GPS PLUGIN SUBMIT', + GPS_GEOLOC_TOOLTIP: 'TEST GPS PLUGIN GEOLOC TOOLTIP', + GPS_NOGEOLOC_TOOLTIP: 'TEST GPS PLUGIN NOGEOLOC TOOLTIP' + }, true); + controls = document.getElementsByClassName('gm-gps-controls')[0]; + map = document.getElementsByClassName('gm-gps-mapview')[0]; + }); + + test('has no speed support by default', () => { + instance = new Instance(); + gps = new GPS(instance, {}); + expect(document.getElementsByClassName('gm-gps-speed')).toHaveLength(0); + }); + + test('is initialized properly at construct', () => { + // Widget views + expect(document.getElementsByClassName('gm-gps-controls')).toHaveLength(1); + expect(document.getElementsByClassName('gm-gps-mapview')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-gps-button')).toHaveLength(1); + }); + + test('has translations', () => { + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN TITLE')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN LATITUDE')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN LONGITUDE')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN ALTITUDE')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN ACCURACY')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN BEARING')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN SPEED')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN GEOLOC')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN SUBMIT')); + expect(controls.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN MAP')); + + expect(map.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN CAPTURE')); + expect(map.innerHTML).toEqual(expect.stringContaining('TEST GPS PLUGIN CANCEL')); + }); + + describe('field validation', () => { + const inputsInvalidValues = [ + ['gm-gps-latitude', 100], + ['gm-gps-longitude', -666], + ['gm-gps-altitude', 23456], + ['gm-gps-accuracy', -2], + ['gm-gps-bearing', 430], + ['gm-gps-speed', 400], + ]; + inputsInvalidValues.forEach(([field, value]) => { + test('button disabled', () => { + const fieldInput = document.getElementsByClassName(field)[0]; + const submitButton = document.getElementsByClassName('gm-gps-submit')[0]; + fieldInput.value = value; + fieldInput.dispatchEvent(new Event('keyup')); + + expect(fieldInput.classList.contains('gm-error')).toBeTruthy(); + expect(submitButton.disabled).toBeTruthy(); + }); + }); + }); + }); + + describe('incoming events', () => { + describe('gps', () => { + test('invalid messages', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'status'); + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'jean-michel'); + expect(setFieldValue).toHaveBeenCalledTimes(0); + }); + + test('altitude', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'altitude'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'altitude jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(15.04444408); + instance.emit('gps', 'altitude -22666'); + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(-10000); + instance.emit('gps', 'altitude 22666'); + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(10000); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'altitude 0'); + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(0); + instance.emit('gps', 'altitude -420'); + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(-420); + instance.emit('gps', 'altitude 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-altitude')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(6); + }); + + test('latitude', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'latitude'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'latitude jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(65.9667); + instance.emit('gps', 'latitude -420'); + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(-90); + instance.emit('gps', 'latitude 666'); + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(90); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'latitude 0'); + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(0); + instance.emit('gps', 'latitude -42'); + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(-42); + instance.emit('gps', 'latitude 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-latitude')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(6); + }); + + test('longitude', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'longitude'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'longitude jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(-18.5333); + instance.emit('gps', 'longitude -420'); + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(-180); + instance.emit('gps', 'longitude 666'); + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(180); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'longitude 0'); + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(0); + instance.emit('gps', 'longitude -123.45'); + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(-123.45); + instance.emit('gps', 'longitude 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-longitude')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(6); + }); + + test('accuracy', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'accuracy'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'accuracy jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-accuracy')[0].value)).toBe(0); + instance.emit('gps', 'accuracy -666'); + expect(Number(document.getElementsByClassName('gm-gps-accuracy')[0].value)).toBe(0); + instance.emit('gps', 'accuracy 420'); + expect(Number(document.getElementsByClassName('gm-gps-accuracy')[0].value)).toBe(200); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'accuracy 42'); + expect(Number(document.getElementsByClassName('gm-gps-accuracy')[0].value)).toBe(42); + instance.emit('gps', 'accuracy 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-accuracy')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(5); + }); + + test('bearing', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'bearing'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'bearing jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-bearing')[0].value)).toBe(0); + instance.emit('gps', 'bearing -22666'); + expect(Number(document.getElementsByClassName('gm-gps-bearing')[0].value)).toBe(0); + instance.emit('gps', 'bearing 22666'); + expect(Number(document.getElementsByClassName('gm-gps-bearing')[0].value)).toBe(360); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'bearing 42'); + expect(Number(document.getElementsByClassName('gm-gps-bearing')[0].value)).toBe(42); + instance.emit('gps', 'bearing 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-bearing')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(5); + }); + + test('speed', () => { + const setFieldValue = jest.spyOn(gps, 'setFieldValue'); + + instance.emit('gps', 'speed'); // Missing value + expect(setFieldValue).toHaveBeenCalledTimes(0); + + instance.emit('gps', 'speed jean-michel'); + expect(Number(document.getElementsByClassName('gm-gps-speed')[0].value)).toBe(0); + instance.emit('gps', 'speed -22666'); + expect(Number(document.getElementsByClassName('gm-gps-speed')[0].value)).toBe(0); + instance.emit('gps', 'speed 300000000'); + expect(Number(document.getElementsByClassName('gm-gps-speed')[0].value)).toBe(399.99); + expect(setFieldValue).toHaveBeenCalledTimes(3); + + instance.emit('gps', 'speed 42'); + expect(Number(document.getElementsByClassName('gm-gps-speed')[0].value)).toBe(42); + instance.emit('gps', 'speed 69'); // Nice + expect(Number(document.getElementsByClassName('gm-gps-speed')[0].value)).toBe(69); + expect(setFieldValue).toHaveBeenCalledTimes(5); + }); + }); + }); + + describe('outgoing events', () => { + let sendEventSpy; + + beforeEach(() => { + sendEventSpy = jest.spyOn(instance, 'sendEvent'); + }); + + afterEach(() => { + sendEventSpy.mockRestore(); + }); + + test('invalid input value', () => { + document.getElementsByClassName('gm-gps-altitude')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-latitude')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-longitude')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-accuracy')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-bearing')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-speed')[0].value = 'jean-michel'; + document.getElementsByClassName('gm-gps-submit')[0].click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'gps', messages: [ + 'set altitude 0', + 'set longitude 0', + 'set latitude 0', + 'set accuracy 0', + 'set bearing 0', + 'set speed 0', + 'enable' + ], + }); + }); + + test('min value', () => { + document.getElementsByClassName('gm-gps-altitude')[0].value = '-22666'; + document.getElementsByClassName('gm-gps-latitude')[0].value = '-420'; + document.getElementsByClassName('gm-gps-longitude')[0].value = '-666'; + document.getElementsByClassName('gm-gps-accuracy')[0].value = '-69'; + document.getElementsByClassName('gm-gps-bearing')[0].value = '-42'; + document.getElementsByClassName('gm-gps-speed')[0].value = '-42'; + document.getElementsByClassName('gm-gps-submit')[0].click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'gps', messages: [ + 'set altitude -10000', + 'set longitude -180', + 'set latitude -90', + 'set accuracy 0', + 'set bearing 0', + 'set speed 0', + 'enable' + ], + }); + }); + + test('max value', () => { + document.getElementsByClassName('gm-gps-altitude')[0].value = '22666'; + document.getElementsByClassName('gm-gps-latitude')[0].value = '420'; + document.getElementsByClassName('gm-gps-longitude')[0].value = '666'; + document.getElementsByClassName('gm-gps-accuracy')[0].value = '6969'; + document.getElementsByClassName('gm-gps-bearing')[0].value = '4242'; + document.getElementsByClassName('gm-gps-speed')[0].value = '300000000'; + document.getElementsByClassName('gm-gps-submit')[0].click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'gps', messages: [ + 'set altitude 10000', + 'set longitude 180', + 'set latitude 90', + 'set accuracy 200', + 'set bearing 360', + 'set speed 399.99', + 'enable' + ], + }); + }); + + test('nominal value', () => { + document.getElementsByClassName('gm-gps-altitude')[0].value = '420'; + document.getElementsByClassName('gm-gps-latitude')[0].value = '69'; // Nice + document.getElementsByClassName('gm-gps-longitude')[0].value = '3.14'; + document.getElementsByClassName('gm-gps-accuracy')[0].value = '42'; + document.getElementsByClassName('gm-gps-bearing')[0].value = '13'; + document.getElementsByClassName('gm-gps-speed')[0].value = '399'; + document.getElementsByClassName('gm-gps-submit')[0].click(); + + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'gps', messages: [ + 'set altitude 420', + 'set longitude 3.14', + 'set latitude 69', + 'set accuracy 42', + 'set bearing 13', + 'set speed 399', + 'enable' + ], + }); + }); + }); +}); diff --git a/tests/unit/identifiers.test.js b/tests/unit/identifiers.test.js new file mode 100644 index 00000000..162ffd11 --- /dev/null +++ b/tests/unit/identifiers.test.js @@ -0,0 +1,185 @@ +'use strict'; + +const Identifiers = require('../../src/plugins/Identifiers'); +const Instance = require('../mocks/GenymotionInstance'); + +let identifiers; +let instance; +let plugin; + +describe('Identifiers Plugin', () => { + beforeEach(() => { + instance = new Instance(); + identifiers = new Identifiers(instance, {}); + plugin = document.getElementsByClassName('gm-identifiers-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Identifiers).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new Identifiers(instance, { + IDENTIFIERS_TITLE: 'TEST IDENTIFIERS PLUGIN TITLE', + IDENTIFIERS_UPDATE: 'TEST IDENTIFIERS PLUGIN UPDATE BUTTON', + IDENTIFIERS_GENERATE: 'TEST IDENTIFIERS PLUGIN GENERATE BUTTON' + }); + plugin = document.getElementsByClassName('gm-identifiers-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-identifiers-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-identifiers-button')).toHaveLength(1); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IDENTIFIERS PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IDENTIFIERS PLUGIN UPDATE BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IDENTIFIERS PLUGIN GENERATE BUTTON')); + }); + + describe('field validation', () => { + ['android', 'device'].forEach((field) => { + test('button disabled', () => { + const fieldInput = document.getElementsByClassName(`gm-identifier-${field}-input`)[0]; + const button = document.getElementsByClassName('gm-action gm-identifiers-update')[0]; + fieldInput.value = 'jean-michel'; + fieldInput.dispatchEvent(new Event('keyup')); + + expect(fieldInput.classList.contains('gm-error')).toBeTruthy(); + expect(button.disabled).toBeTruthy(); + }); + }); + }); + }); + + describe('incoming events', () => { + test('ANDROID_ID', () => { + ['jean-michel', '123', '0123456789abcdef0', ''].forEach((invalidValue) => { + instance.emit('ANDROID_ID', invalidValue); + identifiers.validateAndroidId(); + expect(identifiers.invalidAndroidId).toBeTruthy(); + }); + + ['0123456789abcdef'].forEach((validValue) => { + instance.emit('ANDROID_ID', validValue); + identifiers.validateAndroidId(); + expect(identifiers.invalidAndroidId).toBeFalsy(); + }); + }); + + test('IMEI', () => { + ['jean-michel', '123', '', '0123456789abcdef'].forEach((invalidValue) => { + instance.emit('IMEI', invalidValue); + identifiers.validateDeviceId(); + expect(identifiers.invalidDeviceId).toBeTruthy(); + }); + + ['0123456789abcd', '0123456789abcde'].forEach((validValue) => { + instance.emit('IMEI', validValue); + identifiers.validateDeviceId(); + expect(identifiers.invalidDeviceId).toBeFalsy(); + }); + }); + + describe('settings', () => { + test('unrelevant topics', () => { + ['unrelevant', 'parameters unrelevant:value', ''].forEach((invalidValue) => { + instance.emit('settings', `${invalidValue}`); + identifiers.validateAndroidId(); + identifiers.validateDeviceId(); + expect(identifiers.invalidAndroidId).toBeTruthy(); + expect(identifiers.invalidDeviceId).toBeTruthy(); + }); + }); + + test('android_id', () => { + ['jean-michel', '123', '0123456789abcdef0', ''].forEach((invalidValue) => { + instance.emit('settings', `parameter android_id:${invalidValue}`); + identifiers.validateAndroidId(); + expect(identifiers.invalidAndroidId).toBeTruthy(); + }); + + ['0123456789abcdef'].forEach((validValue) => { + instance.emit('settings', `parameter android_id:${validValue}`); + identifiers.validateAndroidId(); + expect(identifiers.invalidAndroidId).toBeFalsy(); + }); + }); + + test('device_id', () => { + ['jean-michel', '123', '', '0123456789abcdef'].forEach((invalidValue) => { + instance.emit('settings', `parameter device_id:${invalidValue}`); + identifiers.validateDeviceId(); + expect(identifiers.invalidDeviceId).toBeTruthy(); + }); + + ['0123456789abcd', '0123456789abcde'].forEach((validValue) => { + instance.emit('settings', `parameter device_id:${validValue}`); + identifiers.validateDeviceId(); + expect(identifiers.invalidDeviceId).toBeFalsy(); + }); + }); + }); + }); + + test('outgoing events', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + identifiers.androidInput.value = 'jean-michel'; + identifiers.deviceInput.value = 'jean-michel'; + identifiers.sendDataToInstance(new Event('')); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + + identifiers.androidInput.value = 'jean-michel'; + identifiers.deviceInput.value = '0123456789abcde'; + identifiers.sendDataToInstance(new Event('')); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel : 'settings' , messages : [ + 'set parameter device_id:0123456789abcde' + ] + }); + + identifiers.androidInput.value = '0123456789abcdef'; + identifiers.deviceInput.value = 'jean-michel'; + identifiers.sendDataToInstance(new Event('')); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({ + channel : 'framework' , messages : [ + 'set parameter android_id:0123456789abcdef' + ] + }); + + identifiers.androidInput.value = '0123456789abcdef'; + identifiers.deviceInput.value = '0123456789abcde'; + identifiers.sendDataToInstance(new Event('')); + expect(sendEventSpy).toHaveBeenCalledTimes(4); + expect(instance.outgoingMessages[2]).toEqual({ + channel : 'framework' , messages : [ + 'set parameter android_id:0123456789abcdef' + ] + }); + expect(instance.outgoingMessages[3]).toEqual({ + channel : 'settings' , messages : [ + 'set parameter device_id:0123456789abcde' + ] + }); + + identifiers.androidInput.value = '1234567891234@é%'; + identifiers.deviceInput.value = '0123456789abcde'; + identifiers.sendDataToInstance(new Event('')); + expect(sendEventSpy).toHaveBeenCalledTimes(5); + expect(instance.outgoingMessages[4]).toEqual({ + channel : 'settings' , messages : [ + 'set parameter device_id:0123456789abcde' + ] + }); + }); +}); diff --git a/tests/unit/iothrottling.test.js b/tests/unit/iothrottling.test.js new file mode 100644 index 00000000..20bd99cb --- /dev/null +++ b/tests/unit/iothrottling.test.js @@ -0,0 +1,155 @@ +'use strict'; + +const IOThrottling = require('../../src/plugins/IOThrottling'); +const IOThrottlingProfiles = require('../../src/plugins/util/iothrottling-profiles'); +const Instance = require('../mocks/GenymotionInstance'); + +let diskio; +let instance; +let plugin; + +describe('IOThrottling Plugin', () => { + beforeEach(() => { + instance = new Instance(); + diskio = new IOThrottling(instance, {}); + plugin = document.getElementsByClassName('gm-iothrottling-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof IOThrottling).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new IOThrottling(instance, { + IOTHROTTLING_TITLE: 'TEST IOTHROTTLING PLUGIN TITLE', + IOTHROTTLING_PROFILE: 'TEST IOTHROTTLING PLUGIN PROFILE', + IOTHROTTLING_PROFILE_NONE: 'TEST IOTHROTTLING PLUGIN PROFILE NONE', + IOTHROTTLING_READ_BYTERATE: 'TEST IOTHROTTLING PLUGIN READ BYTERATE', + IOTHROTTLING_READ_BYTERATE_EXAMPLE: 'TEST IOTHROTTLING PLUGIN READ BYTERATE EXAMPLE', + IOTHROTTLING_BYTERATE_UNIT: 'TEST IOTHROTTLING PLUGIN BYTERATE UNIT', + IOTHROTTLING_UPDATE: 'TEST IOTHROTTLING PLUGIN UPDATE BUTTON', + IOTHROTTLING_CLEAR_CACHE: 'TEST IOTHROTTLING PLUGIN CLEAR CACHE BUTTON' + }); + plugin = document.getElementsByClassName('gm-iothrottling-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-iothrottling-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-iothrottling-button')).toHaveLength(1); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN PROFILE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN PROFILE NONE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN READ BYTERATE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN READ BYTERATE EXAMPLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN BYTERATE UNIT')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN UPDATE BUTTON')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST IOTHROTTLING PLUGIN CLEAR CACHE BUTTON')); + }); + }); + + describe('incoming events', () => { + test('BLK', () => { + ['jean-michel', '-123', ''].forEach((invalidValue) => { + instance.emit('BLK', invalidValue); + expect(diskio.select.value).toBe('None'); + }); + + [69, 420].forEach((customValue) => { + instance.emit('BLK', customValue * 1024); + expect(diskio.select.value).toBe('Custom'); + expect(Number(diskio.readByteRate.value)).toBe(customValue); + }); + + IOThrottlingProfiles.forEach((profile) => { + if (!profile.readByteRate) { + return; + } + + instance.emit('BLK', profile.readByteRate * 1024); + expect(diskio.select.value).toBe(profile.name); + expect(Number(diskio.readByteRate.value)).toBe(profile.readByteRate); + }); + }); + + test('diskio', () => { + ['jean-michel', 'readbyterate -123', '', 'readbyterate invalid'].forEach((invalidValue) => { + instance.emit('diskio', invalidValue); + expect(diskio.select.value).toBe('None'); + }); + + [69, 420].forEach((customValue) => { + instance.emit('diskio', `readbyterate ${customValue * 1024 * 1024}`); + expect(diskio.select.value).toBe('Custom'); + expect(Number(diskio.readByteRate.value)).toBe(customValue); + }); + + IOThrottlingProfiles.forEach((profile) => { + if (!profile.readByteRate) { + return; + } + + instance.emit('diskio', `readbyterate ${profile.readByteRate * 1024 * 1024}`); + expect(diskio.select.value).toBe(profile.name); + expect(Number(diskio.readByteRate.value)).toBe(profile.readByteRate); + }); + }); + }); + + describe('outgoing events', () => { + test('readbyterate', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + IOThrottlingProfiles.forEach((profile) => { + if (!profile.readByteRate) { + return; + } + + sendEventSpy.mockClear(); + instance.outgoingMessages = []; + diskio.select.value = profile.name; + diskio.select.dispatchEvent(new Event('change')); + diskio.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'diskio', messages: [ + 'set readbyterate ' + profile.readByteRate * 1024 * 1024, + 'clearcache' + ] + }); + }); + + sendEventSpy.mockClear(); + instance.outgoingMessages = []; + diskio.select.value = 'Custom'; + diskio.select.dispatchEvent(new Event('change')); + diskio.readByteRate.value = 69; // Nice + diskio.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'diskio', messages: [ + 'set readbyterate ' + 69 * 1024 * 1024, + 'clearcache' + ] + }); + }); + + test('readbyterate', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + diskio.clearCacheBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({ + channel: 'diskio', messages: ['clearcache'] + }); + }); + }); +}); diff --git a/tests/unit/network.test.js b/tests/unit/network.test.js new file mode 100644 index 00000000..f0dc94c3 --- /dev/null +++ b/tests/unit/network.test.js @@ -0,0 +1,236 @@ +'use strict'; + +const Network = require('../../src/plugins/Network'); +const NetworkProfiles = require('../../src/plugins/util/network-profiles'); +const Instance = require('../mocks/GenymotionInstance'); + +let network; +let instance; +let plugin; + +describe('Network Plugin', () => { + beforeEach(() => { + instance = new Instance(); + network = new Network(instance, {}, true); + plugin = document.getElementsByClassName('gm-network-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Network).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new Network(instance, { + NETWORK_TITLE: 'TEST NETWORK PLUGIN TITLE', + NETWORK_DELECT_PROFILE: 'TEST NETWORK PLUGIN DETECT PROFILE', + NETWORK_OPERATOR: 'TEST NETWORK PLUGIN NETWORK OPERATOR', + NETWORK_SIM_OPERATOR: 'TEST NETWORK PLUGIN SIM OPERATOR', + NETWORK_UPDATE: 'TEST NETWORK PLUGIN UPDATE' + }, true); + plugin = document.getElementsByClassName('gm-network-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-network-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-network-button')).toHaveLength(1); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST NETWORK PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST NETWORK PLUGIN DETECT PROFILE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST NETWORK PLUGIN NETWORK OPERATOR')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST NETWORK PLUGIN SIM OPERATOR')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST NETWORK PLUGIN UPDATE')); + }); + }); + + describe('incoming events', () => { + test('NETWORK', () => { + const loadDetails = jest.spyOn(network, 'loadDetails'); + + ['jean-michel', '-123', '', '9'].forEach((invalidValue) => { + instance.emit('NETWORK', invalidValue); + expect(loadDetails).not.toHaveBeenCalled(); + }); + + NetworkProfiles.forEach((profile) => { + instance.emit('NETWORK', profile.id); + expect(loadDetails).toHaveBeenCalledWith(NetworkProfiles[NetworkProfiles.length - 1 - profile.id]); + loadDetails.mockReset(); + }); + }); + + test('network_profile', () => { + const loadDetails = jest.spyOn(network, 'loadDetails'); + + ['jean-michel', 'state wifi -123', '', 'state wifi missing:additional:values'].forEach((invalidValue) => { + instance.emit('network_profile', invalidValue); + expect(loadDetails).not.toHaveBeenCalled(); + }); + + NetworkProfiles.forEach((profile) => { + let message = 'state wifi '; + message += `up_rate:enabled:${profile.upSpeed.value} `; + message += `down_rate:enabled:${profile.downSpeed.value} `; + message += `up_delay:enabled:${profile.upDelay.value} `; + message += `down_delay:enabled:${profile.downDelay.value} `; + message += `up_pkt_loss:enabled:${profile.upPacketLoss.value} `; + message += `down_pkt_loss:enabled:${profile.downPacketLoss.value} `; + message += `dns_delay:enabled:${profile.dnsDelay.value}`; + instance.emit('network_profile', message); + expect(loadDetails).toHaveBeenCalledWith(NetworkProfiles[NetworkProfiles.length - 1 - profile.id]); + loadDetails.mockReset(); + }); + + let message = 'state wifi '; + message += `up_rate:enabled:${NetworkProfiles[0].upSpeed.value + 1} `; // Unexpected value + message += `down_rate:enabled:${NetworkProfiles[0].downSpeed.value} `; + message += `up_delay:enabled:${NetworkProfiles[0].upDelay.value} `; + message += `down_delay:enabled:${NetworkProfiles[0].downDelay.value} `; + message += `up_pkt_loss:enabled:${NetworkProfiles[0].upPacketLoss.value} `; + message += `down_pkt_loss:enabled:${NetworkProfiles[0].downPacketLoss.value} `; + message += `dns_delay:enabled:${NetworkProfiles[0].dnsDelay.value}`; + instance.emit('network_profile', message); + expect(loadDetails).not.toHaveBeenCalled(); + loadDetails.mockReset(); + + message = 'state wifi '; + message += `up_rate:disabled:${NetworkProfiles[0].upSpeed.value} `; // Disabled value + message += `down_rate:enabled:${NetworkProfiles[0].downSpeed.value} `; + message += `up_delay:enabled:${NetworkProfiles[0].upDelay.value} `; + message += `down_delay:enabled:${NetworkProfiles[0].downDelay.value} `; + message += `up_pkt_loss:enabled:${NetworkProfiles[0].upPacketLoss.value} `; + message += `down_pkt_loss:enabled:${NetworkProfiles[0].downPacketLoss.value} `; + message += `dns_delay:enabled:${NetworkProfiles[0].dnsDelay.value}`; + instance.emit('network_profile', message); + expect(loadDetails).not.toHaveBeenCalled(); + loadDetails.mockReset(); + }); + + describe('baseband', () => { + describe('network', () => { + test('operator', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `network operator ${value}`); + expect(network.networkOperatorMMC.value).toBe(value); + }); + }); + + test('operator_name', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `network operator_name ${value}`); + expect(network.networkOperatorName.value).toBe(value); + }); + }); + }); + + describe('sim', () => { + test('operator', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `sim operator ${value}`); + expect(network.simOperatorMMC.value).toBe(value); + }); + }); + + test('operator_name', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `sim operator_name ${value}`); + expect(network.simOperatorName.value).toBe(value); + }); + }); + + test('imsi_id', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `sim imsi_id ${value}`); + expect(network.simMSIN.value).toBe(value); + }); + }); + + test('phone_number', () => { + ['jean-michel', '-123', ''].forEach((value) => { + instance.emit('baseband', `sim phone_number ${value}`); + expect(network.simOperatorPhoneNumber.value).toBe(value); + }); + }); + }); + }); + }); + + describe('outgoing events', () => { + test('wifi', () => { + instance = new Instance(); + network = new Network(instance, {}, false); + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + NetworkProfiles.forEach((profile) => { + sendEventSpy.mockClear(); + instance.outgoingMessages = []; + const messages = []; + + if (profile.id === 0) { + messages.push('disable wifi all'); + } else { + messages.push('enable wifi all'); + messages.push(`set wifi up_rate ${profile.upSpeed.value}`); + messages.push(`set wifi down_rate ${profile.downSpeed.value}`); + messages.push(`set wifi up_delay ${profile.upDelay.value}`); + messages.push(`set wifi down_delay ${profile.downDelay.value}`); + messages.push(`set wifi up_pkt_loss ${profile.upPacketLoss.value}`); + messages.push(`set wifi down_pkt_loss ${profile.downPacketLoss.value}`); + messages.push(`set wifi dns_delay ${profile.dnsDelay.value}`); + } + + network.setActive(profile.id); + network.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({channel: 'network_profile', messages: messages}); + }); + + sendEventSpy.mockClear(); + network.select.value = 'Select a profile'; + network.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + }); + + test('baseband', () => { + instance = new Instance(); + network = new Network(instance, {}, true); + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + network.select.value = 'Select a profile'; + network.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + + network.networkOperatorMMC.value = '123456'; + network.networkOperatorName.value = 'value'; + network.simOperatorMMC.value = '123456'; + network.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({channel: 'baseband', messages: [ + 'network operator 123456', + 'network operator_name value', + 'sim operator 123456', + ]}); + + network.simOperatorName.value = 'value'; + network.simMSIN.value = '0123456789'; + network.simOperatorPhoneNumber.value = '0011223344'; + network.submitBtn.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({channel: 'baseband', messages: [ + 'network operator 123456', + 'network operator_name value', + 'sim operator 123456', + 'sim operator_name value', + 'sim imsi_id 0123456789', + 'sim phone_number 0011223344' + ]}); + }); + }); +}); diff --git a/tests/unit/phone.test.js b/tests/unit/phone.test.js new file mode 100644 index 00000000..fdce6000 --- /dev/null +++ b/tests/unit/phone.test.js @@ -0,0 +1,92 @@ +'use strict'; + +const Phone = require('../../src/plugins/Phone'); +const Instance = require('../mocks/GenymotionInstance'); + +let phone; +let instance; +let plugin; + +describe('Phone Plugin', () => { + beforeEach(() => { + instance = new Instance(); + phone = new Phone(instance, {}); + plugin = document.getElementsByClassName('gm-phone-plugin')[0]; + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof Phone).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new Phone(instance, { + PHONE_TITLE: 'TEST PHONE PLUGIN TITLE', + PHONE_CALL_PLACEHOLDER: 'TEST PHONE PLUGIN CALL PLACEHOLDER', + PHONE_CALL: 'TEST PHONE PLUGIN CALL', + PHONE_INCOMING: 'TEST PHONE PLUGIN INCOMING', + PHONE_MESSAGE_PLACEHOLDER: 'TEST PHONE PLUGIN MESSAGE PLACEHOLDER', + PHONE_MESSAGE: 'TEST PHONE PLUGIN MESSAGE', + PHONE_MESSAGE_VALUE: 'TEST PHONE PLUGIN MESSAGE_VALUE' + }); + plugin = document.getElementsByClassName('gm-phone-plugin')[0]; + }); + + test('is initialized properly at construct', () => { + // Widget + expect(document.getElementsByClassName('gm-phone-plugin')).toHaveLength(1); + // Toolbar button + expect(document.getElementsByClassName('gm-phone-button')).toHaveLength(1); + }); + + test('has translations', () => { + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN TITLE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN CALL PLACEHOLDER')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN CALL')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN INCOMING')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN MESSAGE PLACEHOLDER')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN MESSAGE')); + expect(plugin.innerHTML).toEqual(expect.stringContaining('TEST PHONE PLUGIN MESSAGE_VALUE')); + }); + }); + + describe('outgoing events', () => { + test('gsm call', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + ['jean-michel', '-666', ''].forEach((invalidValue) => { + phone.phoneInput.value = invalidValue; + phone.sendPhoneCallToInstance(); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + }); + + phone.phoneInput.value = '0123456789'; + phone.sendPhoneCallToInstance(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + + expect(instance.outgoingMessages[0]).toEqual({channel: 'baseband', messages: [ + 'gsm call 0123456789' + ]}); + }); + + test('sms send', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + phone.phoneInput.value = '0123456789'; + + phone.textInput.value = ''; + phone.sendSMSToInstance(); + expect(sendEventSpy).toHaveBeenCalledTimes(0); + + phone.textInput.value = 'Hello world'; + phone.sendSMSToInstance(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + + expect(instance.outgoingMessages[0]).toEqual({channel: 'baseband', messages: [ + 'sms send 0123456789 Hello world' + ]}); + }); + }); +}); diff --git a/tests/unit/streambitrate.test.js b/tests/unit/streambitrate.test.js new file mode 100644 index 00000000..aaff9f37 --- /dev/null +++ b/tests/unit/streambitrate.test.js @@ -0,0 +1,62 @@ +'use strict'; + +const StreamBitrate = require('../../src/plugins/StreamBitrate'); +const Instance = require('../mocks/GenymotionInstance'); + +let chooser; +let instance; + +describe.only('StreamBitrate Plugin', () => { + beforeEach(() => { + instance = new Instance(); + chooser = new StreamBitrate(instance, {}); + }); + + describe('api', () => { + test('exposes a high level constructor', () => { + expect(typeof StreamBitrate).toBe('function'); + }); + }); + + describe('UI', () => { + beforeEach(() => { + instance = new Instance(); + new StreamBitrate(instance, { + STREAMRATE_TITLE: 'TEST QUALITY PLUGIN TITLE' + }); + }); + + test('is initialized properly at construct', () => { + // Toolbar button + expect(document.getElementsByClassName('gm-streamrate-button')).toHaveLength(1); + }); + }); + + describe('outgoing events', () => { + test('hq', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + chooser.highQuality = false; + chooser.chooser.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({type: 'BITRATE', videoBitrate: 5000, audioBitrate: 192000}); + + chooser.chooser.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({type: 'BITRATE', videoBitrate: 0, audioBitrate: 0}); + }); + + test('default', () => { + const sendEventSpy = jest.spyOn(instance, 'sendEvent'); + + chooser.highQuality = true; + chooser.chooser.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(1); + expect(instance.outgoingMessages[0]).toEqual({type: 'BITRATE', videoBitrate: 0, audioBitrate: 0}); + + chooser.chooser.click(); + expect(sendEventSpy).toHaveBeenCalledTimes(2); + expect(instance.outgoingMessages[1]).toEqual({type: 'BITRATE', videoBitrate: 5000, audioBitrate: 192000}); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..df73f390 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,9535 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.5.tgz#f56db0c4bb1bbbf221b4e81345aab4141e7cb0e9" + integrity sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg== + +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.12.1", "@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-compilation-targets@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831" + integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== + dependencies: + "@babel/compat-data" "^7.12.5" + "@babel/helper-validator-option" "^7.12.1" + browserslist "^4.14.5" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-create-regexp-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz#18b1302d4677f9dc4740fe8c9ed96680e29d37e8" + integrity sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.1" + +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz#8006a466695c4ad86a2a5f2fb15b5f2c31ad5633" + integrity sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz#fba0f2fcff3fba00e6ecb664bb5e6e26e2d6165c" + integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-module-imports@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" + +"@babel/helper-remap-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" + integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/types" "^7.12.1" + +"@babel/helper-replace-supers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz#f009a17543bbbbce16b06206ae73b63d3fca68d9" + integrity sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helper-validator-option@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" + integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== + +"@babel/helper-wrap-function@^7.10.4": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz#3332339fc4d1fbbf1c27d7958c27d34708e990d9" + integrity sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.12.3", "@babel/parser@^7.12.5", "@babel/parser@^7.7.0": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" + integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== + +"@babel/plugin-proposal-async-generator-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" + integrity sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-dynamic-import@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc" + integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-export-namespace-from@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz#8b9b8f376b2d88f5dd774e4d24a5cc2e3679b6d4" + integrity sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz#d45423b517714eedd5621a9dfdc03fa9f4eb241c" + integrity sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751" + integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c" + integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz#b1ce757156d40ed79d59d467cb2b154a5c4149ba" + integrity sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-optional-catch-binding@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz#ccc2421af64d3aae50b558a71cede929a5ab2942" + integrity sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz#cce122203fc8a32794296fc377c6dedaf4363797" + integrity sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-private-methods@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389" + integrity sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.12.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz#2a183958d417765b9eae334f47758e5d6a82e072" + integrity sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.1", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.12.1", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0" + integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-arrow-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" + integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" + integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + +"@babel/plugin-transform-block-scoped-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9" + integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-block-scoping@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz#f0ee727874b42a208a48a586b84c3d222c2bbef1" + integrity sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-classes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6" + integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852" + integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-dotall-regex@^7.12.1", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz#a1d16c14862817b6409c0a678d6f9373ca9cd975" + integrity sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-duplicate-keys@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz#745661baba295ac06e686822797a69fbaa2ca228" + integrity sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-exponentiation-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz#b0f2ed356ba1be1428ecaf128ff8a24f02830ae0" + integrity sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-for-of@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa" + integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-function-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667" + integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57" + integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad" + integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-modules-amd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz#3154300b026185666eebb0c0ed7f8415fefcf6f9" + integrity sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz#663fea620d593c93f214a464cd399bf6dc683086" + integrity sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-identifier" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz#eb5a218d6b1c68f3d6217b8fa2cc82fec6547902" + integrity sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz#b407f5c96be0d9f5f88467497fa82b30ac3e8753" + integrity sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + +"@babel/plugin-transform-new-target@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz#80073f02ee1bb2d365c3416490e085c95759dec0" + integrity sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-object-super@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e" + integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + +"@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-property-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd" + integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-regenerator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz#5f0a28d842f6462281f06a964e88ba8d7ab49753" + integrity sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz#6fdfc8cc7edcc42b36a7c12188c6787c873adcd8" + integrity sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-shorthand-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3" + integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e" + integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + +"@babel/plugin-transform-sticky-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz#5c24cf50de396d30e99afc8d1c700e8bce0f5caf" + integrity sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + +"@babel/plugin-transform-template-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843" + integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-typeof-symbol@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz#9ca6be343d42512fbc2e68236a82ae64bc7af78a" + integrity sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-escapes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709" + integrity sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz#cc9661f61390db5c65e3febaccefd5c6ac3faecb" + integrity sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/preset-env@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" + integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== + dependencies: + "@babel/compat-data" "^7.12.1" + "@babel/helper-compilation-targets" "^7.12.1" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-proposal-async-generator-functions" "^7.12.1" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-dynamic-import" "^7.12.1" + "@babel/plugin-proposal-export-namespace-from" "^7.12.1" + "@babel/plugin-proposal-json-strings" "^7.12.1" + "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.12.1" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-computed-properties" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-dotall-regex" "^7.12.1" + "@babel/plugin-transform-duplicate-keys" "^7.12.1" + "@babel/plugin-transform-exponentiation-operator" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-function-name" "^7.12.1" + "@babel/plugin-transform-literals" "^7.12.1" + "@babel/plugin-transform-member-expression-literals" "^7.12.1" + "@babel/plugin-transform-modules-amd" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-modules-systemjs" "^7.12.1" + "@babel/plugin-transform-modules-umd" "^7.12.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" + "@babel/plugin-transform-new-target" "^7.12.1" + "@babel/plugin-transform-object-super" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-property-literals" "^7.12.1" + "@babel/plugin-transform-regenerator" "^7.12.1" + "@babel/plugin-transform-reserved-words" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-unicode-escapes" "^7.12.1" + "@babel/plugin-transform-unicode-regex" "^7.12.1" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.12.1" + core-js-compat "^3.6.2" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + 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" + +"@babel/register@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.12.1.tgz#cdb087bdfc4f7241c03231f22e15d211acf21438" + integrity sha512-XWcmseMIncOjoydKZnWvWi0/5CUCD+ZYKhRwgYlWOrA8fGZ/FjuLRpqtIhLOVD/fvR1b9DQHtZPn68VvhpYf+Q== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/runtime@^7.8.4": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.10.4", "@babel/template@^7.3.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5", "@babel/traverse@^7.7.0": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095" + integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.5" + "@babel/types" "^7.12.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.1", "@babel/types@^7.12.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.5.tgz#5d6b4590cfe90c0c8d7396c83ffd9fc28b5a6450" + integrity sha512-gyTcvz7JFa4V45C0Zklv//GmFOAal5fL23OWpBLqc4nZ4Yrz67s4kCNwSK1Gu0MXGTU8mRY3zJYtacLdKXlzig== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@eslint/eslintrc@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c" + integrity sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + 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" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== + dependencies: + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + +"@sinonjs/commons@^1.7.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.1.12" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d" + integrity sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" + integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214" + integrity sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" + integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== + dependencies: + "@babel/types" "^7.3.0" + +"@types/graceful-fs@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" + integrity sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@^7.0.3": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/node@*": + version "14.14.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f" + integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/prettier@^2.0.0": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" + integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ== + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== + +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + +"@types/yargs@^15.0.0": + version "15.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.9.tgz#524cd7998fe810cdb02f26101b699cccd156ff19" + integrity sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/experimental-utils@^4.0.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.6.1.tgz#a9c691dfd530a9570274fe68907c24c07a06c4aa" + integrity sha512-qyPqCFWlHZXkEBoV56UxHSoXW2qnTr4JrWVXOh3soBP3q0o7p4pUEMfInDwIa0dB/ypdtm7gLOS0hg0a73ijfg== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.6.1" + "@typescript-eslint/types" "4.6.1" + "@typescript-eslint/typescript-estree" "4.6.1" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/scope-manager@4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.6.1.tgz#21872b91cbf7adfc7083f17b8041149148baf992" + integrity sha512-f95+80r6VdINYscJY1KDUEDcxZ3prAWHulL4qRDfNVD0I5QAVSGqFkwHERDoLYJJWmEAkUMdQVvx7/c2Hp+Bjg== + dependencies: + "@typescript-eslint/types" "4.6.1" + "@typescript-eslint/visitor-keys" "4.6.1" + +"@typescript-eslint/types@4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.6.1.tgz#d3ad7478f53f22e7339dc006ab61aac131231552" + integrity sha512-k2ZCHhJ96YZyPIsykickez+OMHkz06xppVLfJ+DY90i532/Cx2Z+HiRMH8YZQo7a4zVd/TwNBuRCdXlGK4yo8w== + +"@typescript-eslint/typescript-estree@4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.6.1.tgz#6025cce724329413f57e4959b2d676fceeca246f" + integrity sha512-/J/kxiyjQQKqEr5kuKLNQ1Finpfb8gf/NpbwqFFYEBjxOsZ621r9AqwS9UDRA1Rrr/eneX/YsbPAIhU2rFLjXQ== + dependencies: + "@typescript-eslint/types" "4.6.1" + "@typescript-eslint/visitor-keys" "4.6.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/visitor-keys@4.6.1": + version "4.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.6.1.tgz#6b125883402d8939df7b54528d879e88f7ba3614" + integrity sha512-owABze4toX7QXwOLT3/D5a8NecZEjEWU1srqxENTfqsY3bwVnl3YYbOh6s1rp2wQKO9RTHFGjKes08FgE7SVMw== + dependencies: + "@typescript-eslint/types" "4.6.1" + eslint-visitor-keys "^2.0.0" + +JSONStream@^1.0.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0, acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +ansi-colors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== + dependencies: + ansi-wrap "^0.1.0" + +ansi-colors@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-2.0.5.tgz#5da37825fef3e75f3bda47f760d64bfd10e15e10" + integrity sha512-yAdfUZ+c2wetVNIFsNRn44THW+Lty6S5TwMpUfLA/UaGhiXbBv/F8E60/1hMLd0cnF/CDoWH8vzVaI5bAcHCjw== + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0, ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" + integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg= + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3, anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" + integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= + dependencies: + buffer-equal "^1.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-filter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" + integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= + dependencies: + make-iterator "^1.0.0" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-map@^2.0.0, arr-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" + integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= + dependencies: + make-iterator "^1.0.0" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= + +array-each@^1.0.0, array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-initial@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" + integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= + dependencies: + array-slice "^1.0.0" + is-number "^4.0.0" + +array-last@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" + integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== + dependencies: + is-number "^4.0.0" + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== + +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-done@^1.2.0, async-done@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" + integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.2" + process-nextick-args "^2.0.0" + stream-exhaust "^1.0.1" + +async-each-series@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/async-each-series/-/async-each-series-0.1.1.tgz#7617c1917401fd8ca4a28aadce3dbae98afeb432" + integrity sha1-dhfBkXQB/Yykooqtzj266Yr+tDI= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-settle@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" + integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= + dependencies: + async-done "^1.2.2" + +async@1.5.2, async@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^9.6.1: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axios@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz#cf5feef29551253471cfa82fc8e0f5063df07a77" + integrity sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q== + 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" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +bach@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" + integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= + dependencies: + arr-filter "^1.1.1" + arr-flatten "^1.0.1" + arr-map "^2.0.0" + array-each "^1.0.0" + array-initial "^1.0.0" + array-last "^1.1.1" + async-done "^1.2.2" + async-settle "^1.0.0" + now-and-later "^2.0.0" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= + +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base64id@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" + integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= + +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= + dependencies: + inherits "~2.0.0" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +body@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" + integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= + dependencies: + continuable-cache "^0.3.1" + error "^7.0.0" + raw-body "~1.1.0" + safe-json-parse "~1.0.1" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-pack@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" + integrity sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA== + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.8.0" + defined "^1.0.0" + safe-buffer "^5.1.1" + through2 "^2.0.0" + umd "^3.0.0" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browser-resolve@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" + integrity sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ== + dependencies: + resolve "^1.17.0" + +browser-sync-client@^2.26.13: + version "2.26.13" + resolved "https://registry.yarnpkg.com/browser-sync-client/-/browser-sync-client-2.26.13.tgz#ee5fa3ec36fe2a03f9887553cac6846751c8232d" + integrity sha512-p2VbZoYrpuDhkreq+/Sv1MkToHklh7T1OaIntDwpG6Iy2q/XkBcgwPcWjX+WwRNiZjN8MEehxIjEUh12LweLmQ== + dependencies: + etag "1.8.1" + fresh "0.5.2" + mitt "^1.1.3" + rxjs "^5.5.6" + +browser-sync-ui@^2.26.13: + version "2.26.13" + resolved "https://registry.yarnpkg.com/browser-sync-ui/-/browser-sync-ui-2.26.13.tgz#7a0622df2c1cc4fb0dd8edd511f90737f84239b4" + integrity sha512-6NJ/pCnhCnBMzaty1opWo7ipDmFAIk8U71JMQGKJxblCUaGfdsbF2shf6XNZSkXYia1yS0vwKu9LIOzpXqQZCA== + dependencies: + async-each-series "0.1.1" + connect-history-api-fallback "^1" + immutable "^3" + server-destroy "1.0.1" + socket.io-client "^2.0.4" + stream-throttle "^0.1.3" + +browser-sync@^2.26.13: + version "2.26.13" + resolved "https://registry.yarnpkg.com/browser-sync/-/browser-sync-2.26.13.tgz#a74541c104aec7eda318a5d8abdb3317ae9eda3d" + integrity sha512-JPYLTngIzI+Dzx+StSSlMtF+Q9yjdh58HW6bMFqkFXuzQkJL8FCvp4lozlS6BbECZcsM2Gmlgp0uhEjvl18X4w== + dependencies: + browser-sync-client "^2.26.13" + browser-sync-ui "^2.26.13" + bs-recipes "1.3.4" + bs-snippet-injector "^2.0.1" + chokidar "^3.4.1" + connect "3.6.6" + connect-history-api-fallback "^1" + dev-ip "^1.0.1" + easy-extender "^2.3.4" + eazy-logger "3.1.0" + etag "^1.8.1" + fresh "^0.5.2" + fs-extra "3.0.1" + http-proxy "^1.18.1" + immutable "^3" + localtunnel "^2.0.0" + micromatch "^4.0.2" + opn "5.3.0" + portscanner "2.1.1" + qs "6.2.3" + raw-body "^2.3.2" + resp-modifier "6.0.2" + rx "4.1.0" + send "0.16.2" + serve-index "1.9.1" + serve-static "1.13.2" + server-destroy "1.0.1" + socket.io "2.1.1" + ua-parser-js "^0.7.18" + yargs "^15.4.1" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-transform-tools@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/browserify-transform-tools/-/browserify-transform-tools-1.7.0.tgz#83e277221f63259bed2e7eb2a283a970a501f4c4" + integrity sha1-g+J3Ih9jJZvtLn6yooOpcKUB9MQ= + dependencies: + falafel "^2.0.0" + through "^2.3.7" + +browserify-zlib@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserify@^17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-17.0.0.tgz#4c48fed6c02bfa2b51fd3b670fddb805723cdc22" + integrity sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w== + dependencies: + JSONStream "^1.0.3" + assert "^1.4.0" + browser-pack "^6.0.1" + browser-resolve "^2.0.0" + browserify-zlib "~0.2.0" + buffer "~5.2.1" + cached-path-relative "^1.0.0" + concat-stream "^1.6.0" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.1" + domain-browser "^1.2.0" + duplexer2 "~0.1.2" + events "^3.0.0" + glob "^7.1.0" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "^1.0.0" + inherits "~2.0.1" + insert-module-globals "^7.2.1" + labeled-stream-splicer "^2.0.0" + mkdirp-classic "^0.5.2" + module-deps "^6.2.3" + os-browserify "~0.3.0" + parents "^1.0.1" + path-browserify "^1.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum-object "^1.0.0" + shell-quote "^1.6.1" + stream-browserify "^3.0.0" + stream-http "^3.0.0" + string_decoder "^1.1.1" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "0.0.1" + url "~0.11.0" + util "~0.12.0" + vm-browserify "^1.0.0" + xtend "^4.0.0" + +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.8.5: + version "4.14.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.6.tgz#97702a9c212e0c6b6afefad913d3a1538e348457" + integrity sha512-zeFYcUo85ENhc/zxHbiIp0LGzzTrE2Pv2JhxvS7kpUb9Q9D38kUX6Bie7pGutJ/5iF5rOxE7CepAuWD56xJ33A== + dependencies: + caniuse-lite "^1.0.30001154" + electron-to-chromium "^1.3.585" + escalade "^3.1.1" + node-releases "^1.1.65" + +bs-recipes@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/bs-recipes/-/bs-recipes-1.3.4.tgz#0d2d4d48a718c8c044769fdc4f89592dc8b69585" + integrity sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU= + +bs-snippet-injector@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz#61b5393f11f52559ed120693100343b6edb04dd5" + integrity sha1-YbU5PxH1JVntEgaTEANDtu2wTdU= + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" + integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@~5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +buffers@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" + integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" + integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== + +call-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" + integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.0" + +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001154: + version "1.0.30001271" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz" + integrity sha512-BBruZFWmt3HFdVPS8kceTBIguKxu4f99n5JNp06OlPD/luoAMIaIK5ieV5YjnBLH3Nysai9sxj9rpJj4ZisXOA== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" + integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8= + dependencies: + ansi-styles "~1.0.0" + has-color "~0.1.0" + strip-ansi "~0.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chokidar@^2.0.0: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@4.2.3, clean-css@4.2.x: + version "4.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" + integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-color@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.4.0.tgz#7d10738f48526824f8fe7da51857cb0f572fe01f" + integrity sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w== + dependencies: + ansi-regex "^2.1.1" + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + memoizee "^0.4.14" + timers-ext "^0.1.5" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= + +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + +clone@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +cloneable-readable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== + dependencies: + inherits "^2.0.1" + process-nextick-args "^2.0.0" + readable-stream "^2.3.5" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" + integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= + dependencies: + arr-map "^2.0.2" + for-own "^1.0.0" + make-iterator "^1.0.0" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + +combine-source-map@^0.8.0, combine-source-map@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" + integrity sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos= + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.17.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +commander@^2.2.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@~2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + +component-emitter@^1.2.1, component-emitter@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-with-sourcemaps@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + +connect-history-api-fallback@^1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +connect-livereload@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/connect-livereload/-/connect-livereload-0.6.1.tgz#1ac0c8bb9d9cfd5b28b629987a56a9239db9baaa" + integrity sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g== + +connect@3.6.6: + version "3.6.6" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" + integrity sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ= + dependencies: + debug "2.6.9" + finalhandler "1.1.0" + parseurl "~1.3.2" + utils-merge "1.0.1" + +connect@^3.6.6: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constants-browserify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +continuable-cache@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" + integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= + +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +convert-source-map@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" + integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-props@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" + integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== + dependencies: + each-props "^1.3.0" + is-plain-object "^2.0.1" + +core-js-compat@^3.6.2: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== + dependencies: + browserslist "^4.8.5" + semver "7.0.0" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.0.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dash-ast@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" + integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +dateformat@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" + integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decimal.js@^10.2.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + +default-resolution@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" + integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deps-sort@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d" + integrity sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw== + dependencies: + JSONStream "^1.0.3" + shasum-object "^1.0.0" + subarg "^1.0.0" + through2 "^2.0.0" + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +detective@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" + integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== + dependencies: + acorn-node "^1.6.1" + defined "^1.0.0" + minimist "^1.1.1" + +dev-ip@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" + integrity sha1-p2o+0YVb56ASu4rBbLgPPADcKPA= + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +domain-browser@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= + dependencies: + readable-stream "~1.1.9" + +duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +duplexify@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" + integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + +each-props@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" + integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== + dependencies: + is-plain-object "^2.0.1" + object.defaults "^1.1.0" + +easy-extender@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/easy-extender/-/easy-extender-2.3.4.tgz#298789b64f9aaba62169c77a2b3b64b4c9589b8f" + integrity sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q== + dependencies: + lodash "^4.17.10" + +eazy-logger@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eazy-logger/-/eazy-logger-3.1.0.tgz#b169eb56df714608fa114f164c8a2956bec9f0f3" + integrity sha512-/snsn2JqBtUSSstEl4R0RKjkisGHAhvYj89i7r3ytNUKW12y178KDZwXLXIgwDqLW6E/VRMT9qfld7wvFae8bQ== + dependencies: + tfunk "^4.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.585: + version "1.3.587" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.587.tgz#de570df7320eb259c0511f284c2d6008094edbf7" + integrity sha512-8XFNxzNj0R8HpTQslWAw6UWpGSuOKSP3srhyFHVbGUGb8vTHckZGCyWi+iQlaXJx5DNeTQTQLd6xN11WSckkmA== + +elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.1, encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +engine.io-client@~3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36" + integrity sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.1.1" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~3.3.1" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-client@~3.4.0: + version "3.4.4" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967" + integrity sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6" + integrity sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.4" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io@~3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.2.1.tgz#b60281c35484a70ee0351ea0ebff83ec8c9522a2" + integrity sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w== + dependencies: + accepts "~1.3.4" + base64id "1.0.0" + cookie "0.3.1" + debug "~3.1.0" + engine.io-parser "~2.1.0" + ws "~3.3.1" + +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" + integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== + dependencies: + string-template "~0.2.1" + +es-abstract@^1.17.4, es-abstract@^1.17.5: + version "1.17.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1, es6-weak-map@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^1.14.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-plugin-jest@^24.1.0: + version "24.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.0.tgz#6708037d7602e5288ce877fd0103f329dc978361" + integrity sha512-827YJ+E8B9PvXu/0eiVSNFfxxndbKv+qE/3GSMhdorCaeaOehtqHGX2YDW9B85TEOre9n/zscledkFW/KbnyGg== + dependencies: + "@typescript-eslint/experimental-utils" "^4.0.1" + +eslint-scope@^5.0.0, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^7.12.1: + version "7.12.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.12.1.tgz#bd9a81fa67a6cfd51656cdb88812ce49ccec5801" + integrity sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.2.1" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.19" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" + integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse-fb@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/estraverse-fb/-/estraverse-fb-1.3.2.tgz#d323a4cb5e5ac331cea033413a9253e1643e07c4" + integrity sha1-0yOky15awzHOoDNBOpJT4WQ+B8Q= + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@1.8.1, etag@^1.8.1, etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +falafel@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.2.4.tgz#b5d86c060c2412a43166243cb1bce44d1abd2819" + integrity sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ== + dependencies: + acorn "^7.1.1" + foreach "^2.0.5" + isarray "^2.0.1" + object-keys "^1.0.6" + +fancy-log@^1.1.0, fancy-log@^1.3.2, fancy-log@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.1.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" + integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fastq@^1.6.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947" + integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== + dependencies: + reusify "^1.0.4" + +faye-websocket@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + integrity sha1-zgtoVbRYU+eRsvzGgARtiCU91/U= + dependencies: + debug "2.6.9" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.3.1" + unpipe "~1.0.0" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + 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" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +findup-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +fined@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" + integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +flagged-respawn@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" + integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +flow-parser@^0.*: + version "0.137.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.137.0.tgz#8c05612ff9344648d8bcdeaa4c58d131e875d842" + integrity sha512-i3KXJZ8lhlQI0n+BoZzIeH/rv+fNvAiu1i9/s64MklBV+HuhFbycUML7367J2eng0gapLnwvYPFNaPZys8POsA== + +flush-write-stream@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +follow-redirects@^1.0.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-stream@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" + integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2, fresh@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" + integrity sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + +fs-mkdirp-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" + integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= + dependencies: + graceful-fs "^4.1.11" + through2 "^2.0.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.2.0.tgz#4150396a427ecb5baa747725b70270a72b17bc17" + integrity sha512-pKnaUh2TNvk+/egJdBw1h46LwyLx8BzEq+MGCf/RMCVfEHHsGOCWG00dqk91kUPPArIIwMBg9T/virxwzP03cA== + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fstream@^1.0.0, fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== + dependencies: + globule "^1.0.0" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-assigned-identifiers@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" + integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" + integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +get-value@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" + integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== + dependencies: + isobject "^3.0.1" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob-stream@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" + integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= + dependencies: + extend "^3.0.0" + glob "^7.1.1" + glob-parent "^3.1.0" + is-negated-glob "^1.0.0" + ordered-read-streams "^1.0.0" + pumpify "^1.3.5" + readable-stream "^2.1.5" + remove-trailing-separator "^1.0.1" + to-absolute-glob "^2.0.0" + unique-stream "^2.0.2" + +glob-watcher@^5.0.3: + version "5.0.5" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" + integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== + dependencies: + anymatch "^2.0.0" + async-done "^1.2.0" + chokidar "^2.0.0" + is-negated-glob "^1.0.0" + just-debounce "^1.0.0" + normalize-path "^3.0.0" + object.defaults "^1.1.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + 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" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +globule@^1.0.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" + integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== + dependencies: + glob "~7.1.1" + lodash "~4.17.10" + minimatch "~3.0.2" + +glogg@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" + integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== + dependencies: + sparkles "^1.0.0" + +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +grasp-equery@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/grasp-equery/-/grasp-equery-0.4.0.tgz#5016329c1eecacfe3671cede7c9bc0fc99b10d7b" + integrity sha1-UBYynB7srP42cc7efJvA/JmxDXs= + dependencies: + flow-parser "^0.*" + grasp-syntax-javascript "^0.2.0" + prelude-ls "^1.1.2" + +grasp-squery@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/grasp-squery/-/grasp-squery-0.4.0.tgz#05593a7e836eba80a3465f9603f2c366aaf5397f" + integrity sha1-BVk6foNuuoCjRl+WA/LDZqr1OX8= + dependencies: + grasp-syntax-javascript "~0.2.0" + prelude-ls "~1.1.2" + +grasp-syntax-javascript@^0.2.0, grasp-syntax-javascript@~0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/grasp-syntax-javascript/-/grasp-syntax-javascript-0.2.2.tgz#660ffd8ffb1eb108f77447594ac37e72b2b3504c" + integrity sha1-Zg/9j/sesQj3dEdZSsN+crKzUEw= + dependencies: + prelude-ls "~1.1.2" + +grasp@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/grasp/-/grasp-0.6.0.tgz#7ecf1e26479cb4dec899b898a8afdf8b7d83fc53" + integrity sha1-fs8eJkectN7ImbiYqK/fi32D/FM= + dependencies: + async "^1.5.0" + cli-color "^1.1.0" + flow-parser "^0.*" + grasp-equery "^0.4.0" + grasp-squery "^0.4.0" + grasp-syntax-javascript "^0.2.0" + levn "^0.3.0" + minimatch "^3.0.3" + optionator "^0.8.0" + prelude-ls "^1.1.2" + slash "^1.0.0" + +group-array@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/group-array/-/group-array-1.0.0.tgz#e2e8d8890e5b46f72eb49b71e8af675173a9d0f7" + integrity sha512-PJresALe5TUzSIcdWKLdAKcdUDxv8du2EGueShgAL2xknbcTo5Bk1xbNaNhxpWxxAx/SV7N+5S0UyK7XV0+QhA== + dependencies: + arr-flatten "^1.1.0" + for-own "^1.0.0" + get-value "^3.0.1" + kind-of "^6.0.2" + split-string "^6.1.0" + union-value "^2.0.1" + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gulp-autoprefixer@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/gulp-autoprefixer/-/gulp-autoprefixer-7.0.1.tgz#3c0dc26afc802d317e7560a7f760a0399049075a" + integrity sha512-QJGEmHw+bEt7FSqvmbAUTxbCuNLJYx4sz3ox9WouYqT/7j5FH5CQ8ZnpL1M7H5npX1bUJa7lUVY1w20jXxhOxg== + dependencies: + autoprefixer "^9.6.1" + fancy-log "^1.3.2" + plugin-error "^1.0.1" + postcss "^7.0.17" + through2 "^3.0.1" + vinyl-sourcemaps-apply "^0.2.1" + +gulp-babel@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/gulp-babel/-/gulp-babel-8.0.0.tgz#e0da96f4f2ec4a88dd3a3030f476e38ab2126d87" + integrity sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ== + dependencies: + plugin-error "^1.0.1" + replace-ext "^1.0.0" + through2 "^2.0.0" + vinyl-sourcemaps-apply "^0.2.0" + +gulp-clean-css@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gulp-clean-css/-/gulp-clean-css-4.3.0.tgz#5b1e73f2fca46703eb636014cdd4553cea65146d" + integrity sha512-mGyeT3qqFXTy61j0zOIciS4MkYziF2U594t2Vs9rUnpkEHqfu6aDITMp8xOvZcvdX61Uz3y1mVERRYmjzQF5fg== + dependencies: + clean-css "4.2.3" + plugin-error "1.0.1" + through2 "3.0.1" + vinyl-sourcemaps-apply "0.2.1" + +gulp-cli@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" + integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== + dependencies: + ansi-colors "^1.0.1" + archy "^1.0.0" + array-sort "^1.0.0" + color-support "^1.1.3" + concat-stream "^1.6.0" + copy-props "^2.0.1" + fancy-log "^1.3.2" + gulplog "^1.0.0" + interpret "^1.4.0" + isobject "^3.0.1" + liftoff "^3.1.0" + matchdep "^2.0.0" + mute-stdout "^1.0.0" + pretty-hrtime "^1.0.0" + replace-homedir "^1.0.0" + semver-greatest-satisfied-range "^1.1.0" + v8flags "^3.2.0" + yargs "^7.1.0" + +gulp-concat@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" + integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= + dependencies: + concat-with-sourcemaps "^1.0.0" + through2 "^2.0.0" + vinyl "^2.0.0" + +gulp-connect@^5.0.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/gulp-connect/-/gulp-connect-5.7.0.tgz#7e925f5e4c34ebfedf9f318576966e8fe8840d5a" + integrity sha512-8tRcC6wgXMLakpPw9M7GRJIhxkYdgZsXwn7n56BA2bQYGLR9NOPhMzx7js+qYDy6vhNkbApGKURjAw1FjY4pNA== + dependencies: + ansi-colors "^2.0.5" + connect "^3.6.6" + connect-livereload "^0.6.0" + fancy-log "^1.3.2" + map-stream "^0.0.7" + send "^0.16.2" + serve-index "^1.9.1" + serve-static "^1.13.2" + tiny-lr "^1.1.1" + +gulp-css-base64@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gulp-css-base64/-/gulp-css-base64-2.0.0.tgz#a3984789ccda2079b075ee2b323213945812a8e2" + integrity sha512-8ZtWdWEzBCL4Pa/kNCNssXRlXzQmz9fmrJRf2q5fBwg5OzBB1DTs7hXvAZMQMCoKgQ+HLEylw97OIEXALHdtGw== + dependencies: + async "^1.5.0" + buffers "^0.1.1" + chalk "^1.1.1" + fancy-log "^1.3.3" + mime "^1.3.4" + plugin-error "^1.0.1" + request "^2.67.0" + through2 "^2.0.0" + vinyl "^2.2.0" + +gulp-html-to-js@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/gulp-html-to-js/-/gulp-html-to-js-0.0.7.tgz#c59ebde458ec4be55d84cc17c8a4061b417b9c5a" + integrity sha512-P4iQ9roXfI15M6MXNo2cJvOEWI6VdIZZTeLmqtqMt0JhXihh2vO0EtuCvzFID+lTG3HtG0jbw1vPTFQzbCxUFw== + +gulp-htmlmin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz#90fc5e8ad0425a9e86d5d521427184e7276365e7" + integrity sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA== + dependencies: + html-minifier "^3.5.20" + plugin-error "^1.0.1" + through2 "^2.0.3" + +gulp-if@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-3.0.0.tgz#6c3e7edc8bafadc34f2ebecb314bf43324ba1e40" + integrity sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw== + dependencies: + gulp-match "^1.1.0" + ternary-stream "^3.0.0" + through2 "^3.0.1" + +gulp-inject@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/gulp-inject/-/gulp-inject-5.0.5.tgz#c23df9cbf331447b6e13a1498cc51b63a7ceef67" + integrity sha512-5bGMjqleXUHPu4CI1pnVzHtwyMy+Zt8EMo1RFwNsOpidPxwjFwyLgmsRZWGMMI8UenJMJRjURqwznfFmqb5wgw== + dependencies: + ansi-colors "^4.1.1" + arrify "^2.0.1" + escape-string-regexp "^2.0.0" + fancy-log "^1.3.3" + group-array "^1.0.0" + plugin-error "^1.0.1" + stream-to-array "^2.3.0" + through2 "^3.0.1" + +gulp-match@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.1.0.tgz#552b7080fc006ee752c90563f9fec9d61aafdf4f" + integrity sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ== + dependencies: + minimatch "^3.0.3" + +gulp-sass@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-4.1.0.tgz#486d7443c32d42bf31a6b1573ebbdaa361de7427" + integrity sha512-xIiwp9nkBLcJDpmYHbEHdoWZv+j+WtYaKD6Zil/67F3nrAaZtWYN5mDwerdo7EvcdBenSAj7Xb2hx2DqURLGdA== + dependencies: + chalk "^2.3.0" + lodash "^4.17.11" + node-sass "^4.8.3" + plugin-error "^1.0.1" + replace-ext "^1.0.0" + strip-ansi "^4.0.0" + through2 "^2.0.0" + vinyl-sourcemaps-apply "^0.2.0" + +gulp-streamify@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/gulp-streamify/-/gulp-streamify-1.0.2.tgz#00d6b3814d486c088f78738ed0766abc16389e4d" + integrity sha1-ANazgU1IbAiPeHOO0HZqvBY4nk0= + dependencies: + plexer "1.0.1" + +gulp-tap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gulp-tap/-/gulp-tap-2.0.0.tgz#6f66b79870dcbfc364cf4ebe0735b6008473200f" + integrity sha512-U5/v1bTozx672QHzrvzPe6fPl2io7Wqyrx2y30AG53eMU/idH4BrY/b2yikOkdyhjDqGgPoMUMnpBg9e9LK8Nw== + dependencies: + through2 "^3.0.1" + +gulp-uglify-es@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gulp-uglify-es/-/gulp-uglify-es-2.0.0.tgz#0a48d9f352393397e78cf2be44bf0fb66a353031" + integrity sha512-00KkawzjWdjPo1YfD1FXKijVxZkyr6YSwJ2cJQgD1fNKFZCFPNjGc5sTyzyW8tZns8FmZafgHMrg7LUDNvIQ5A== + dependencies: + o-stream "^0.2.2" + plugin-error "^1.0.1" + terser "^4.3.9" + vinyl "^2.2.0" + vinyl-sourcemaps-apply "^0.2.1" + +gulp-using@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/gulp-using/-/gulp-using-0.1.1.tgz#6f5a6827fdf4c364a31cbdecd425202aa5a268e7" + integrity sha1-b1poJ/30w2SjHL3s1CUgKqWiaOc= + dependencies: + chalk "~0.4.0" + map-stream "~0.1.0" + pretty-bytes "^3.0.1" + +gulp-util@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" + integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^2.0.0" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulp@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" + integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== + dependencies: + glob-watcher "^5.0.3" + gulp-cli "^2.2.0" + undertaker "^1.2.1" + vinyl-fs "^3.0.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= + dependencies: + glogg "^1.0.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-color@~0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8= + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= + dependencies: + sparkles "^1.0.0" + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier@^3.5.20: + version "3.5.21" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" + integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== + dependencies: + camel-case "3.0.x" + clean-css "4.2.x" + commander "2.17.x" + he "1.2.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +htmlescape@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" + integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E= + +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" + integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +immutable@^3: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" + integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e" + integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +in-publish@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c" + integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inline-source-map@~0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" + integrity sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU= + dependencies: + source-map "~0.5.3" + +insert-module-globals@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.1.tgz#d5e33185181a4e1f33b15f7bf100ee91890d5cb3" + integrity sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg== + dependencies: + JSONStream "^1.0.3" + acorn-node "^1.5.2" + combine-source-map "^0.8.0" + concat-stream "^1.6.1" + is-buffer "^1.1.0" + path-is-absolute "^1.0.1" + process "~0.11.0" + through2 "^2.0.0" + undeclared-identifiers "^1.1.2" + xtend "^4.0.0" + +interpret@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.0, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d" + integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" + integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-negated-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" + integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= + +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + +is-number-like@^1.0.3: + version "1.0.8" + resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.8.tgz#2e129620b50891042e44e9bbbb30593e75cfbbe3" + integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA== + dependencies: + lodash.isfinite "^3.3.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" + integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + +is-promise@^2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== + dependencies: + is-unc-path "^1.0.0" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typed-array@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" + integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + dependencies: + available-typed-arrays "^1.0.0" + es-abstract "^1.17.4" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== + dependencies: + unc-path-regex "^0.1.2" + +is-utf8@^0.2.0, is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-valid-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" + integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@^0.1.2, isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" + prompts "^2.0.1" + yargs "^15.4.1" + +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.6.2" + is-generator-fn "^2.0.0" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-junit@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.0.0.tgz#3ebd4a6a84b50c4ab18323a8f7d9cceb9d845df6" + integrity sha512-+8K35LlboWiPuCnXSyiid7rFdxNlpCWWM20WEYe6IZH6psfUWKZmSpSRQ5tk0C0cBeDsvsnIzcef5mYhyJsbug== + dependencies: + mkdirp "^1.0.4" + strip-ansi "^5.2.0" + uuid "^3.3.3" + xml "^1.0.1" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== + dependencies: + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.6.2" + +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== + dependencies: + "@jest/core" "^26.6.3" + import-local "^3.0.2" + jest-cli "^26.6.3" + +js-base64@^2.1.8: + version "2.6.4" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" + integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" + integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== + dependencies: + abab "^2.0.3" + acorn "^7.1.1" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.2.0" + data-urls "^2.0.0" + decimal.js "^10.2.0" + domexception "^2.0.1" + escodegen "^1.14.1" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" + nwsapi "^2.2.0" + parse5 "5.1.1" + request "^2.88.2" + request-promise-native "^1.0.8" + saxes "^5.0.0" + symbol-tree "^3.2.4" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + ws "^7.2.3" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +just-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" + integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0, kind-of@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +labeled-stream-splicer@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" + integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw== + dependencies: + inherits "^2.0.1" + stream-splicer "^2.0.0" + +last-run@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" + integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= + dependencies: + default-resolution "^2.0.0" + es6-weak-map "^2.0.1" + +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lead@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" + integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= + dependencies: + flush-write-stream "^1.0.2" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +liftoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" + integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== + dependencies: + extend "^3.0.0" + findup-sync "^3.0.0" + fined "^1.0.1" + flagged-respawn "^1.0.0" + is-plain-object "^2.0.4" + object.map "^1.0.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +limiter@^1.0.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" + integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +livereload-js@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" + integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +localtunnel@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-2.0.0.tgz#2ea71174fa80e34cce91b2a7ce416e6a57d9ff7c" + integrity sha512-g6E0aLgYYDvQDxIjIXkgJo2+pHj3sGg4Wz/XP3h2KtZnRsWPbOQY+hw1H8Z91jep998fkcVE9l+kghO+97vllg== + dependencies: + axios "0.19.0" + debug "4.1.1" + openurl "1.1.1" + yargs "13.3.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= + dependencies: + lodash._root "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + +lodash.isfinite@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3" + integrity sha1-+4m2WpqAKBgz8LdHizpRBPiY67M= + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.10: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +loglevel@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== + dependencies: + kind-of "^6.0.2" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-stream@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" + integrity sha1-ih8HiW2CsQkmvTdEokIACfiJdKg= + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +matchdep@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" + integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= + dependencies: + findup-sync "^2.0.0" + micromatch "^3.0.4" + resolve "^1.4.0" + stack-trace "0.0.10" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memoizee@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" + integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== + dependencies: + d "1" + es5-ext "^0.10.45" + es6-weak-map "^2.0.2" + event-emitter "^0.3.5" + is-promise "^2.1" + lru-queue "0.1" + next-tick "1" + timers-ext "^0.1.5" + +meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== + +mime@1.6.0, mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mitt@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" + integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +module-deps@^6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee" + integrity sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA== + dependencies: + JSONStream "^1.0.3" + browser-resolve "^2.0.0" + cached-path-relative "^1.0.2" + concat-stream "~1.6.0" + defined "^1.0.0" + detective "^5.2.0" + duplexer2 "^0.1.2" + inherits "^2.0.1" + parents "^1.0.0" + readable-stream "^2.0.2" + resolve "^1.4.0" + stream-combiner2 "^1.1.1" + subarg "^1.0.0" + through2 "^2.0.0" + xtend "^4.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= + dependencies: + duplexer2 "0.0.2" + +mute-stdout@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" + integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== + +nan@^2.12.1, nan@^2.13.2: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-gyp@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" + integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +node-releases@^1.1.65: + version "1.1.65" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.65.tgz#52d9579176bd60f23eba05c4438583f341944b81" + integrity sha512-YpzJOe2WFIW0V4ZkJQd/DGR/zdVwc/pI4Nl1CZrBO19FdRcSTmsuhdttw9rsTzzJLrNcSloLiBbEYx1C4f6gpA== + +node-sass@^4.8.3: + version "4.14.1" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" + integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash "^4.17.15" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.13.2" + node-gyp "^3.8.0" + npmlog "^4.0.0" + request "^2.88.0" + sass-graph "2.2.5" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +now-and-later@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" + integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== + dependencies: + once "^1.3.2" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +o-stream@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/o-stream/-/o-stream-0.2.2.tgz#7fe03af870b8f9537af33b312b381b3034ab410f" + integrity sha512-V3j76KU3g/Gyl8rpdi2z72rn5zguMvTCQgAXfBe3pxEefKqXmOUOD7mvx/mNjykdxGqDVfpSoo8r+WdrkWg/1Q== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.defaults@^1.0.0, object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.reduce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" + integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +openurl@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/openurl/-/openurl-1.1.1.tgz#3875b4b0ef7a52c156f0db41d4609dbb0f94b387" + integrity sha1-OHW0sO96UsFW8NtB1GCduw+Us4c= + +opn@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + integrity sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g== + dependencies: + is-wsl "^1.1.0" + +optionator@^0.8.0, optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + 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" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + 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" + +ordered-read-streams@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" + integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= + dependencies: + readable-stream "^2.0.1" + +os-browserify@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@0: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-each-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" + integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parents@^1.0.0, parents@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= + dependencies: + path-platform "~0.11.15" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-filepath@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" + integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + 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" + +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + +parse5@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + dependencies: + path-root-regex "^0.1.0" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pbkdf2@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.0, pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +plexer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/plexer/-/plexer-1.0.1.tgz#a801b652bf8145739795ea4d3bf0af946c30c0dd" + integrity sha1-qAG2Ur+BRXOXlepNO/CvlGwwwN0= + dependencies: + isstream "^0.1.2" + readable-stream "^2.0.2" + +plugin-error@1.0.1, plugin-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" + integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== + dependencies: + ansi-colors "^1.0.1" + arr-diff "^4.0.0" + arr-union "^3.1.0" + extend-shallow "^3.0.2" + +portscanner@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.1.1.tgz#eabb409e4de24950f5a2a516d35ae769343fbb96" + integrity sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y= + dependencies: + async "1.5.2" + is-number-like "^1.0.3" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^7.0.17, postcss@^7.0.32: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prelude-ls@^1.1.2, prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-bytes@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf" + integrity sha1-J9AAjXeAY6C0gRuzXHnxvV1fvM8= + dependencies: + number-is-nan "^1.0.0" + +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +pretty-hrtime@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@~0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prompts@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" + integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4= + +qs@^6.4.0: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring-es3@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.0, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^2.3.2: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@~1.1.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" + integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= + dependencies: + bytes "1" + string_decoder "0.10" + +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" + integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + +read-only-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" + integrity sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A= + dependencies: + readable-stream "^2.0.2" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + 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" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-bom-buffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" + integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== + dependencies: + is-buffer "^1.1.5" + is-utf8 "^0.2.1" + +remove-bom-stream@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" + integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= + dependencies: + remove-bom-buffer "^3.0.0" + safe-buffer "^5.1.0" + through2 "^2.0.3" + +remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= + +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + +replace-homedir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" + integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= + dependencies: + homedir-polyfill "^1.0.1" + is-absolute "^1.0.0" + remove-trailing-separator "^1.1.0" + +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.67.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-options@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" + integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= + dependencies: + value-or-function "^3.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.3.2, resolve@^1.4.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" + integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== + dependencies: + is-core-module "^2.0.0" + path-parse "^1.0.6" + +resp-modifier@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f" + integrity sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08= + dependencies: + debug "^2.2.0" + minimatch "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +rtcpeerconnection-shim@^1.2.15: + version "1.2.15" + resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz#e7cc189a81b435324c4949aa3dfb51888684b243" + integrity sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw== + dependencies: + sdp "^2.6.0" + +run-parallel@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" + integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + +rx@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= + +rxjs@^5.5.6: + version "5.5.12" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" + integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== + dependencies: + symbol-observable "1.0.1" + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-json-parse@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" + integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sass-graph@2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.5.tgz#a981c87446b8319d96dce0671e487879bd24c2e8" + integrity sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag== + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^13.3.2" + +saxes@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +sdp@^2.12.0, sdp@^2.6.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.12.0.tgz#338a106af7560c86e4523f858349680350d53b22" + integrity sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw== + +semver-greatest-satisfied-range@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" + integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= + dependencies: + sver-compat "^1.5.0" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + +send@0.16.2, send@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== + 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.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + 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.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-index@1.9.1, serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + 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" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +serve-static@^1.13.2: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +server-destroy@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + integrity sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0= + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +set-value@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90" + integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA== + dependencies: + is-plain-object "^2.0.4" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shasum-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shasum-object/-/shasum-object-1.0.0.tgz#0b7b74ff5b66ecf9035475522fa05090ac47e29e" + integrity sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg== + dependencies: + fast-safe-stringify "^2.0.7" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.6.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +socket.io-adapter@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" + integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== + +socket.io-client@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.1.1.tgz#dcb38103436ab4578ddb026638ae2f21b623671f" + integrity sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~3.1.0" + engine.io-client "~3.2.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.2.0" + to-array "0.1.4" + +socket.io-client@^2.0.4: + version "2.3.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.1.tgz#91a4038ef4d03c19967bb3c646fec6e0eaa78cff" + integrity sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ== + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "~1.3.0" + debug "~3.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077" + integrity sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + +socket.io-parser@~3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199" + integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ== + dependencies: + component-emitter "~1.3.0" + debug "~3.1.0" + isarray "2.0.1" + +socket.io@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980" + integrity sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA== + dependencies: + debug "~3.1.0" + engine.io "~3.2.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.1.1" + socket.io-parser "~3.2.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + 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" + +source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + integrity sha1-66T12pwNyZneaAMti092FzZSA2s= + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@~0.5.3: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +sparkles@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" + integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" + integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split-string@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-6.1.0.tgz#e9cedcf94cdab077d9b5528927894dec4b0f42ab" + integrity sha512-9UBdnmnvx2NLLd4bMs7CEKK+wSzbujVv3ONyorkP1o8M3pVJQtXDO1cN19xD1JJs6ltOrtPrkUND0HzLSinUcA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-trace@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + +stack-utils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" + integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + +stdout-stream@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" + integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== + dependencies: + readable-stream "^2.0.1" + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-combiner2@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= + dependencies: + duplexer2 "~0.1.0" + readable-stream "^2.0.2" + +stream-exhaust@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" + integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== + +stream-http@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564" + integrity sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-splicer@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd" + integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.2" + +stream-throttle@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/stream-throttle/-/stream-throttle-0.1.3.tgz#add57c8d7cc73a81630d31cd55d3961cfafba9c3" + integrity sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM= + dependencies: + commander "^2.2.0" + limiter "^1.0.5" + +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M= + dependencies: + any-promise "^1.1.0" + +string-length@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" + integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-template@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" + integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" + integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +string.prototype.trimstart@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7" + integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +string_decoder@0.10, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-ansi@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" + integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE= + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= + dependencies: + minimist "^1.1.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" + integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +sver-compat@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" + integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= + dependencies: + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +syntax-error@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" + integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w== + dependencies: + acorn-node "^1.2.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tar@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== + dependencies: + block-stream "*" + fstream "^1.0.12" + inherits "2" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +ternary-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ternary-stream/-/ternary-stream-3.0.0.tgz#7951930ea9e823924d956f03d516151a2d516253" + integrity sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ== + dependencies: + duplexify "^4.1.1" + fork-stream "^0.0.4" + merge-stream "^2.0.0" + through2 "^3.0.1" + +terser@^4.3.9: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +tfunk@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tfunk/-/tfunk-4.0.0.tgz#de9399feaf2060901d590b7faad80fcd5443077e" + integrity sha512-eJQ0dGfDIzWNiFNYFVjJ+Ezl/GmwHaFTBTjrtqNPW0S7cuVDBrZrmzUz6VkMeCR4DZFqhd4YtLwsw3i2wYHswQ== + dependencies: + chalk "^1.1.3" + dlv "^1.1.3" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" + integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== + dependencies: + readable-stream "2 || 3" + +through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + +through2@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +"through@>=2.2.7 <3", through@^2.3.7: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= + +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= + dependencies: + process "~0.11.0" + +timers-ext@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +tiny-lr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" + integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== + dependencies: + body "^5.1.0" + debug "^3.1.0" + faye-websocket "~0.10.0" + livereload-js "^2.3.0" + object-assign "^4.1.0" + qs "^6.4.0" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-absolute-glob@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" + integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= + dependencies: + is-absolute "^1.0.0" + is-negated-glob "^1.0.0" + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-through@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" + integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= + dependencies: + through2 "^2.0.3" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + dependencies: + punycode "^2.1.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +"true-case-path@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" + integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew== + dependencies: + glob "^7.1.2" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +ua-parser-js@^0.7.18: + version "0.7.22" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3" + integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q== + +uglify-js@3.4.x: + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== + dependencies: + commander "~2.19.0" + source-map "~0.6.1" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +umd@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" + integrity sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow== + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + +undeclared-identifiers@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz#9254c1d37bdac0ac2b52de4b6722792d2a91e30f" + integrity sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw== + dependencies: + acorn-node "^1.3.0" + dash-ast "^1.0.0" + get-assigned-identifiers "^1.2.0" + simple-concat "^1.0.0" + xtend "^4.0.1" + +undertaker-registry@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" + integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= + +undertaker@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" + integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== + dependencies: + arr-flatten "^1.0.1" + arr-map "^2.0.0" + bach "^1.0.0" + collection-map "^1.0.0" + es6-weak-map "^2.0.1" + fast-levenshtein "^1.0.0" + last-run "^1.1.0" + object.defaults "^1.0.0" + object.reduce "^1.0.0" + undertaker-registry "^1.0.0" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +union-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-2.0.1.tgz#4e1ab0f99ab52c52a53e14d8039b5130fef682b8" + integrity sha512-NmcRHHhUy1qWmp6yYWsaURV2qwfS24TmTtO9S9x0L41wCNNVBQFD3toOzO0cd8SsNrFhbw/O0iYO5uffXGYocw== + dependencies: + get-value "^3.0.1" + set-value "^3.0.0" + +unique-stream@^2.0.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" + integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== + dependencies: + json-stable-stringify-without-jsonify "^1.0.1" + through2-filter "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@~0.12.0: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== + +v8-compile-cache@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" + integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== + +v8-to-istanbul@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz#b4fe00e35649ef7785a9b7fcebcea05f37c332fc" + integrity sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +v8flags@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== + dependencies: + homedir-polyfill "^1.0.1" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +value-or-function@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" + integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" + integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== + dependencies: + fs-mkdirp-stream "^1.0.0" + glob-stream "^6.1.0" + graceful-fs "^4.0.0" + is-valid-glob "^1.0.0" + lazystream "^1.0.0" + lead "^1.0.0" + object.assign "^4.0.4" + pumpify "^1.3.5" + readable-stream "^2.3.3" + remove-bom-buffer "^3.0.0" + remove-bom-stream "^1.2.0" + resolve-options "^1.1.0" + through2 "^2.0.0" + to-through "^2.0.0" + value-or-function "^3.0.0" + vinyl "^2.0.0" + vinyl-sourcemap "^1.1.0" + +vinyl-source-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz#f38a5afb9dd1e93b65d550469ac6182ac4f54b8e" + integrity sha1-84pa+53R6Ttl1VBGmsYYKsT1S44= + dependencies: + through2 "^2.0.3" + vinyl "^2.1.0" + +vinyl-sourcemap@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" + integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= + dependencies: + append-buffer "^1.0.2" + convert-source-map "^1.5.0" + graceful-fs "^4.1.6" + normalize-path "^2.1.1" + now-and-later "^2.0.0" + remove-bom-buffer "^3.0.0" + vinyl "^2.0.0" + +vinyl-sourcemaps-apply@0.2.1, vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" + integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= + dependencies: + source-map "^0.5.1" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vinyl@^2.0.0, vinyl@^2.1.0, vinyl@^2.2.0, vinyl@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" + integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== + dependencies: + clone "^2.1.1" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + +vm-browserify@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +webrtc-adapter@^7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-7.7.0.tgz#e56ff25f925177ac553a7c49323ca4108d2b5f4d" + integrity sha512-7Bp9OBnx642oJRkom1tNAbeJjUadAq2rh5xLL9YXPw5hVyt2h4hHr5bcoPYDs1stp/mZHSPSQA34YISdnr0DBQ== + dependencies: + rtcpeerconnection-shim "^1.2.15" + sdp "^2.12.0" + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" + integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^2.0.2" + webidl-conversions "^6.1.0" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" + integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + dependencies: + available-typed-arrays "^1.0.2" + es-abstract "^1.17.5" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@1, which@^1.2.14, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^7.2.3: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + +ws@~3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xml@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yargs-parser@5.0.0-security.0: + version "5.0.0-security.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24" + integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== + dependencies: + camelcase "^3.0.0" + object.assign "^4.1.0" + +yargs-parser@^13.1.1, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + 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" + +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6" + integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "5.0.0-security.0" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=