From 0031f92c543399eae74c4035d16311f28edbd4ed Mon Sep 17 00:00:00 2001 From: feeling Date: Tue, 12 Jan 2021 12:26:58 +0100 Subject: [PATCH] init --- .clang-format | 16 + .gitignore | 9 + .travis.yml | 19 + LICENSE.txt | 165 + README.md | 676 +- factory_settings.ini | 54 + features.ini | 8 + interface/.env | 5 + interface/.env.development | 4 + interface/.env.production | 1 + interface/config-overrides.js | 37 + interface/package-lock.json | 14652 ++++++++++++++++ interface/package.json | 57 + interface/progmem-generator.js | 122 + interface/public/app/icon.png | Bin 0 -> 8940 bytes interface/public/app/manifest.json | 12 + interface/public/css/roboto.css | 22 + interface/public/favicon.ico | Bin 0 -> 1150 bytes interface/public/fonts/li.woff2 | Bin 0 -> 15440 bytes interface/public/fonts/me.woff2 | Bin 0 -> 15552 bytes interface/public/fonts/re.woff2 | Bin 0 -> 15344 bytes interface/public/index.html | 16 + interface/src/App.tsx | 50 + interface/src/AppRouting.tsx | 60 + interface/src/CustomMuiTheme.tsx | 39 + interface/src/SignIn.tsx | 147 + interface/src/ap/APModes.ts | 5 + interface/src/ap/APSettingsController.tsx | 30 + interface/src/ap/APSettingsForm.tsx | 147 + interface/src/ap/APStatus.ts | 28 + interface/src/ap/APStatusController.tsx | 29 + interface/src/ap/APStatusForm.tsx | 78 + interface/src/ap/AccessPoint.tsx | 38 + interface/src/ap/types.ts | 30 + interface/src/api/Endpoints.ts | 22 + interface/src/api/Env.ts | 24 + interface/src/api/index.ts | 2 + .../src/authentication/AuthenticatedRoute.tsx | 42 + .../src/authentication/Authentication.ts | 114 + .../authentication/AuthenticationContext.tsx | 59 + .../authentication/AuthenticationWrapper.tsx | 109 + .../authentication/UnauthenticatedRoute.tsx | 30 + interface/src/authentication/index.ts | 6 + interface/src/components/ApplicationError.tsx | 59 + .../src/components/BlockFormControlLabel.tsx | 10 + interface/src/components/ErrorButton.tsx | 11 + interface/src/components/FormActions.tsx | 7 + interface/src/components/FormButton.tsx | 13 + .../src/components/FullScreenLoading.tsx | 32 + interface/src/components/HighlightAvatar.tsx | 23 + interface/src/components/MenuAppBar.tsx | 286 + .../src/components/PasswordValidator.tsx | 58 + interface/src/components/RestController.tsx | 113 + interface/src/components/RestFormLoader.tsx | 56 + interface/src/components/SectionContent.tsx | 33 + interface/src/components/SingleUpload.tsx | 96 + .../src/components/WebSocketController.tsx | 133 + .../src/components/WebSocketFormLoader.tsx | 40 + interface/src/components/index.ts | 17 + interface/src/features/ApplicationContext.tsx | 23 + interface/src/features/FeaturesContext.tsx | 27 + interface/src/features/FeaturesWrapper.tsx | 61 + interface/src/features/types.ts | 8 + interface/src/history.ts | 5 + interface/src/index.tsx | 13 + interface/src/mqtt/Mqtt.tsx | 37 + interface/src/mqtt/MqttSettingsController.tsx | 30 + interface/src/mqtt/MqttSettingsForm.tsx | 128 + interface/src/mqtt/MqttStatus.ts | 45 + interface/src/mqtt/MqttStatusController.tsx | 29 + interface/src/mqtt/MqttStatusForm.tsx | 83 + interface/src/mqtt/types.ts | 29 + interface/src/ntp/NTPSettingsController.tsx | 30 + interface/src/ntp/NTPSettingsForm.tsx | 80 + interface/src/ntp/NTPStatus.ts | 26 + interface/src/ntp/NTPStatusController.tsx | 30 + interface/src/ntp/NTPStatusForm.tsx | 198 + interface/src/ntp/NetworkTime.tsx | 39 + interface/src/ntp/TZ.tsx | 479 + interface/src/ntp/TimeFormat.ts | 5 + interface/src/ntp/types.ts | 23 + interface/src/project/DemoInformation.tsx | 77 + interface/src/project/DemoProject.tsx | 43 + .../project/LightMqttSettingsController.tsx | 90 + .../src/project/LightStateRestController.tsx | 67 + .../project/LightStateWebSocketController.tsx | 62 + interface/src/project/ProjectMenu.tsx | 27 + interface/src/project/ProjectRouting.tsx | 33 + interface/src/project/types.ts | 9 + interface/src/react-app-env.d.ts | 1 + .../src/security/ManageUsersController.tsx | 30 + interface/src/security/ManageUsersForm.tsx | 184 + interface/src/security/Security.tsx | 37 + .../security/SecuritySettingsController.tsx | 30 + .../src/security/SecuritySettingsForm.tsx | 52 + interface/src/security/UserForm.tsx | 86 + interface/src/security/types.ts | 11 + interface/src/serviceWorker.ts | 145 + .../src/system/OTASettingsController.tsx | 30 + interface/src/system/OTASettingsForm.tsx | 66 + interface/src/system/System.tsx | 51 + .../src/system/SystemStatusController.tsx | 30 + interface/src/system/SystemStatusForm.tsx | 245 + .../src/system/UploadFirmwareController.tsx | 71 + interface/src/system/UploadFirmwareForm.tsx | 35 + interface/src/system/types.ts | 37 + interface/src/validators/index.ts | 4 + interface/src/validators/isHostname.ts | 6 + interface/src/validators/isIP.ts | 5 + interface/src/validators/optional.ts | 1 + interface/src/validators/or.ts | 3 + interface/src/wifi/WiFiConnection.tsx | 62 + interface/src/wifi/WiFiConnectionContext.tsx | 13 + interface/src/wifi/WiFiNetworkScanner.tsx | 168 + interface/src/wifi/WiFiNetworkSelector.tsx | 54 + interface/src/wifi/WiFiSecurityModes.ts | 21 + interface/src/wifi/WiFiSettingsController.tsx | 29 + interface/src/wifi/WiFiSettingsForm.tsx | 200 + interface/src/wifi/WiFiStatus.ts | 41 + interface/src/wifi/WiFiStatusController.tsx | 29 + interface/src/wifi/WiFiStatusForm.tsx | 117 + interface/src/wifi/types.ts | 56 + interface/tsconfig.json | 25 + lib/framework/APSettingsService.cpp | 83 + lib/framework/APSettingsService.h | 146 + lib/framework/APStatus.cpp | 22 + lib/framework/APStatus.h | 31 + lib/framework/ArduinoJsonJWT.cpp | 144 + lib/framework/ArduinoJsonJWT.h | 37 + lib/framework/AuthenticationService.cpp | 48 + lib/framework/AuthenticationService.h | 30 + lib/framework/ESP8266React.cpp | 114 + lib/framework/ESP8266React.h | 122 + lib/framework/ESPFS.h | 7 + lib/framework/FSPersistence.h | 100 + lib/framework/FactoryResetService.cpp | 37 + lib/framework/FactoryResetService.h | 32 + lib/framework/Features.h | 37 + lib/framework/FeaturesService.cpp | 42 + lib/framework/FeaturesService.h | 29 + lib/framework/HttpEndpoint.h | 165 + lib/framework/JsonUtils.h | 29 + lib/framework/MqttPubSub.h | 167 + lib/framework/MqttSettingsService.cpp | 161 + lib/framework/MqttSettingsService.h | 148 + lib/framework/MqttStatus.cpp | 24 + lib/framework/MqttStatus.h | 31 + lib/framework/NTPSettingsService.cpp | 90 + lib/framework/NTPSettingsService.h | 84 + lib/framework/NTPStatus.cpp | 40 + lib/framework/NTPStatus.h | 31 + lib/framework/OTASettingsService.cpp | 71 + lib/framework/OTASettingsService.h | 72 + lib/framework/RestartService.cpp | 13 + lib/framework/RestartService.h | 31 + lib/framework/SecurityManager.h | 97 + lib/framework/SecuritySettingsService.cpp | 140 + lib/framework/SecuritySettingsService.h | 119 + lib/framework/SettingValue.cpp | 55 + lib/framework/SettingValue.h | 14 + lib/framework/StatefulService.cpp | 3 + lib/framework/StatefulService.h | 148 + lib/framework/SystemStatus.cpp | 45 + lib/framework/SystemStatus.h | 29 + lib/framework/UploadFirmwareService.cpp | 85 + lib/framework/UploadFirmwareService.h | 38 + lib/framework/WebSocketTxRx.h | 273 + lib/framework/WiFiScanner.cpp | 70 + lib/framework/WiFiScanner.h | 35 + lib/framework/WiFiSettingsService.cpp | 100 + lib/framework/WiFiSettingsService.h | 112 + lib/framework/WiFiStatus.cpp | 75 + lib/framework/WiFiStatus.h | 45 + lib/readme.txt | 36 + media/build.png | Bin 0 -> 8646 bytes media/dark.png | Bin 0 -> 64313 bytes media/devserver.png | Bin 0 -> 73249 bytes media/esp12e.jpg | Bin 0 -> 17509 bytes media/esp32.jpg | Bin 0 -> 26212 bytes media/framework.png | Bin 0 -> 58594 bytes media/screenshots.png | Bin 0 -> 66132 bytes media/uploadfs.png | Bin 0 -> 8644 bytes media/uploadfw.png | Bin 0 -> 8823 bytes platformio.ini | 49 + scripts/build_interface.py | 34 + src/LightMqttSettingsService.cpp | 16 + src/LightMqttSettingsService.h | 41 + src/LightStateService.cpp | 73 + src/LightStateService.h | 88 + src/main.cpp | 36 + 190 files changed, 25835 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE.txt create mode 100644 factory_settings.ini create mode 100644 features.ini create mode 100644 interface/.env create mode 100644 interface/.env.development create mode 100644 interface/.env.production create mode 100644 interface/config-overrides.js create mode 100644 interface/package-lock.json create mode 100644 interface/package.json create mode 100644 interface/progmem-generator.js create mode 100644 interface/public/app/icon.png create mode 100644 interface/public/app/manifest.json create mode 100644 interface/public/css/roboto.css create mode 100644 interface/public/favicon.ico create mode 100644 interface/public/fonts/li.woff2 create mode 100644 interface/public/fonts/me.woff2 create mode 100644 interface/public/fonts/re.woff2 create mode 100644 interface/public/index.html create mode 100644 interface/src/App.tsx create mode 100644 interface/src/AppRouting.tsx create mode 100644 interface/src/CustomMuiTheme.tsx create mode 100644 interface/src/SignIn.tsx create mode 100644 interface/src/ap/APModes.ts create mode 100644 interface/src/ap/APSettingsController.tsx create mode 100644 interface/src/ap/APSettingsForm.tsx create mode 100644 interface/src/ap/APStatus.ts create mode 100644 interface/src/ap/APStatusController.tsx create mode 100644 interface/src/ap/APStatusForm.tsx create mode 100644 interface/src/ap/AccessPoint.tsx create mode 100644 interface/src/ap/types.ts create mode 100644 interface/src/api/Endpoints.ts create mode 100644 interface/src/api/Env.ts create mode 100644 interface/src/api/index.ts create mode 100644 interface/src/authentication/AuthenticatedRoute.tsx create mode 100644 interface/src/authentication/Authentication.ts create mode 100644 interface/src/authentication/AuthenticationContext.tsx create mode 100644 interface/src/authentication/AuthenticationWrapper.tsx create mode 100644 interface/src/authentication/UnauthenticatedRoute.tsx create mode 100644 interface/src/authentication/index.ts create mode 100644 interface/src/components/ApplicationError.tsx create mode 100644 interface/src/components/BlockFormControlLabel.tsx create mode 100644 interface/src/components/ErrorButton.tsx create mode 100644 interface/src/components/FormActions.tsx create mode 100644 interface/src/components/FormButton.tsx create mode 100644 interface/src/components/FullScreenLoading.tsx create mode 100644 interface/src/components/HighlightAvatar.tsx create mode 100644 interface/src/components/MenuAppBar.tsx create mode 100644 interface/src/components/PasswordValidator.tsx create mode 100644 interface/src/components/RestController.tsx create mode 100644 interface/src/components/RestFormLoader.tsx create mode 100644 interface/src/components/SectionContent.tsx create mode 100644 interface/src/components/SingleUpload.tsx create mode 100644 interface/src/components/WebSocketController.tsx create mode 100644 interface/src/components/WebSocketFormLoader.tsx create mode 100644 interface/src/components/index.ts create mode 100644 interface/src/features/ApplicationContext.tsx create mode 100644 interface/src/features/FeaturesContext.tsx create mode 100644 interface/src/features/FeaturesWrapper.tsx create mode 100644 interface/src/features/types.ts create mode 100644 interface/src/history.ts create mode 100644 interface/src/index.tsx create mode 100644 interface/src/mqtt/Mqtt.tsx create mode 100644 interface/src/mqtt/MqttSettingsController.tsx create mode 100644 interface/src/mqtt/MqttSettingsForm.tsx create mode 100644 interface/src/mqtt/MqttStatus.ts create mode 100644 interface/src/mqtt/MqttStatusController.tsx create mode 100644 interface/src/mqtt/MqttStatusForm.tsx create mode 100644 interface/src/mqtt/types.ts create mode 100644 interface/src/ntp/NTPSettingsController.tsx create mode 100644 interface/src/ntp/NTPSettingsForm.tsx create mode 100644 interface/src/ntp/NTPStatus.ts create mode 100644 interface/src/ntp/NTPStatusController.tsx create mode 100644 interface/src/ntp/NTPStatusForm.tsx create mode 100644 interface/src/ntp/NetworkTime.tsx create mode 100644 interface/src/ntp/TZ.tsx create mode 100644 interface/src/ntp/TimeFormat.ts create mode 100644 interface/src/ntp/types.ts create mode 100644 interface/src/project/DemoInformation.tsx create mode 100644 interface/src/project/DemoProject.tsx create mode 100644 interface/src/project/LightMqttSettingsController.tsx create mode 100644 interface/src/project/LightStateRestController.tsx create mode 100644 interface/src/project/LightStateWebSocketController.tsx create mode 100644 interface/src/project/ProjectMenu.tsx create mode 100644 interface/src/project/ProjectRouting.tsx create mode 100644 interface/src/project/types.ts create mode 100644 interface/src/react-app-env.d.ts create mode 100644 interface/src/security/ManageUsersController.tsx create mode 100644 interface/src/security/ManageUsersForm.tsx create mode 100644 interface/src/security/Security.tsx create mode 100644 interface/src/security/SecuritySettingsController.tsx create mode 100644 interface/src/security/SecuritySettingsForm.tsx create mode 100644 interface/src/security/UserForm.tsx create mode 100644 interface/src/security/types.ts create mode 100644 interface/src/serviceWorker.ts create mode 100644 interface/src/system/OTASettingsController.tsx create mode 100644 interface/src/system/OTASettingsForm.tsx create mode 100644 interface/src/system/System.tsx create mode 100644 interface/src/system/SystemStatusController.tsx create mode 100644 interface/src/system/SystemStatusForm.tsx create mode 100644 interface/src/system/UploadFirmwareController.tsx create mode 100644 interface/src/system/UploadFirmwareForm.tsx create mode 100644 interface/src/system/types.ts create mode 100644 interface/src/validators/index.ts create mode 100644 interface/src/validators/isHostname.ts create mode 100644 interface/src/validators/isIP.ts create mode 100644 interface/src/validators/optional.ts create mode 100644 interface/src/validators/or.ts create mode 100644 interface/src/wifi/WiFiConnection.tsx create mode 100644 interface/src/wifi/WiFiConnectionContext.tsx create mode 100644 interface/src/wifi/WiFiNetworkScanner.tsx create mode 100644 interface/src/wifi/WiFiNetworkSelector.tsx create mode 100644 interface/src/wifi/WiFiSecurityModes.ts create mode 100644 interface/src/wifi/WiFiSettingsController.tsx create mode 100644 interface/src/wifi/WiFiSettingsForm.tsx create mode 100644 interface/src/wifi/WiFiStatus.ts create mode 100644 interface/src/wifi/WiFiStatusController.tsx create mode 100644 interface/src/wifi/WiFiStatusForm.tsx create mode 100644 interface/src/wifi/types.ts create mode 100644 interface/tsconfig.json create mode 100644 lib/framework/APSettingsService.cpp create mode 100644 lib/framework/APSettingsService.h create mode 100644 lib/framework/APStatus.cpp create mode 100644 lib/framework/APStatus.h create mode 100644 lib/framework/ArduinoJsonJWT.cpp create mode 100644 lib/framework/ArduinoJsonJWT.h create mode 100644 lib/framework/AuthenticationService.cpp create mode 100644 lib/framework/AuthenticationService.h create mode 100644 lib/framework/ESP8266React.cpp create mode 100644 lib/framework/ESP8266React.h create mode 100644 lib/framework/ESPFS.h create mode 100644 lib/framework/FSPersistence.h create mode 100644 lib/framework/FactoryResetService.cpp create mode 100644 lib/framework/FactoryResetService.h create mode 100644 lib/framework/Features.h create mode 100644 lib/framework/FeaturesService.cpp create mode 100644 lib/framework/FeaturesService.h create mode 100644 lib/framework/HttpEndpoint.h create mode 100644 lib/framework/JsonUtils.h create mode 100644 lib/framework/MqttPubSub.h create mode 100644 lib/framework/MqttSettingsService.cpp create mode 100644 lib/framework/MqttSettingsService.h create mode 100644 lib/framework/MqttStatus.cpp create mode 100644 lib/framework/MqttStatus.h create mode 100644 lib/framework/NTPSettingsService.cpp create mode 100644 lib/framework/NTPSettingsService.h create mode 100644 lib/framework/NTPStatus.cpp create mode 100644 lib/framework/NTPStatus.h create mode 100644 lib/framework/OTASettingsService.cpp create mode 100644 lib/framework/OTASettingsService.h create mode 100644 lib/framework/RestartService.cpp create mode 100644 lib/framework/RestartService.h create mode 100644 lib/framework/SecurityManager.h create mode 100644 lib/framework/SecuritySettingsService.cpp create mode 100644 lib/framework/SecuritySettingsService.h create mode 100644 lib/framework/SettingValue.cpp create mode 100644 lib/framework/SettingValue.h create mode 100644 lib/framework/StatefulService.cpp create mode 100644 lib/framework/StatefulService.h create mode 100644 lib/framework/SystemStatus.cpp create mode 100644 lib/framework/SystemStatus.h create mode 100644 lib/framework/UploadFirmwareService.cpp create mode 100644 lib/framework/UploadFirmwareService.h create mode 100644 lib/framework/WebSocketTxRx.h create mode 100644 lib/framework/WiFiScanner.cpp create mode 100644 lib/framework/WiFiScanner.h create mode 100644 lib/framework/WiFiSettingsService.cpp create mode 100644 lib/framework/WiFiSettingsService.h create mode 100644 lib/framework/WiFiStatus.cpp create mode 100644 lib/framework/WiFiStatus.h create mode 100644 lib/readme.txt create mode 100644 media/build.png create mode 100644 media/dark.png create mode 100644 media/devserver.png create mode 100644 media/esp12e.jpg create mode 100644 media/esp32.jpg create mode 100644 media/framework.png create mode 100644 media/screenshots.png create mode 100644 media/uploadfs.png create mode 100644 media/uploadfw.png create mode 100644 platformio.ini create mode 100644 scripts/build_interface.py create mode 100644 src/LightMqttSettingsService.cpp create mode 100644 src/LightMqttSettingsService.h create mode 100644 src/LightStateService.cpp create mode 100644 src/LightStateService.h create mode 100644 src/main.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4e688ba --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +Language: Cpp +BasedOnStyle: Google +ColumnLimit: 120 +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +BinPackParameters: false +BreakConstructorInitializers: AfterColon +AllowAllParametersOfDeclarationOnNextLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ExperimentalAutoDetectBinPacking: false +KeepEmptyLinesAtTheStartOfBlocks: false +DerivePointerAlignment: false +SortIncludes: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..facc712 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.pio +.clang_complete +.gcc-flags.json +*Thumbs.db +/data/www +/lib/framework/WWWData.h +/interface/build +/interface/node_modules +.vscode diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..30aa006 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: python +python: + - "3.8" + +before_install: + - nvm install 10.15.3 + - nvm use 10.15.3 + +sudo: false +cache: + directories: + - "~/.platformio" + +install: + - pip install -U platformio + - platformio update + +script: + - platformio run -e esp12e -e node32s diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md index 30d74d2..9921b15 100644 --- a/README.md +++ b/README.md @@ -1 +1,675 @@ -test \ No newline at end of file +# ESP8266 React + +[![Build Status](https://travis-ci.org/rjwats/esp8266-react.svg?branch=master)](https://travis-ci.org/rjwats/esp8266-react) + +A simple, secure and extensible framework for IoT projects built on ESP8266/ESP32 platforms with responsive [React](https://reactjs.org/) front-end built with [Material-UI](https://material-ui.com/). + +Designed to work with the PlatformIO IDE with [limited setup](#getting-started). Please read below for setup, build and upload instructions. + +![Screenshots](/media/screenshots.png?raw=true "Screenshots") + +## Features + +Provides many of the features required for IoT projects: + +* Configurable WiFi - Network scanner and WiFi configuration screen +* Configurable Access Point - Can be continuous or automatically enabled when WiFi connection fails +* Network Time - Synchronization with NTP +* MQTT - Connection to an MQTT broker for automation and monitoring +* Remote Firmware Updates - Firmware replacement using OTA update or upload via UI +* Security - Protected RESTful endpoints and a secured user interface + +Features may be [enabled or disabled](#selecting-features) as required at compile time. + +## Getting Started + +### Prerequisites + +You will need the following before you can get started. + +* [PlatformIO](https://platformio.org/) - IDE for development +* [Node.js](https://nodejs.org) - For building the interface with npm + +### Building and uploading the firmware + +Pull the project and open it in PlatformIO. PlatformIO should download the ESP8266 platform and the project library dependencies automatically. + +The project structure is as follows: + +Resource | Description +-------------------------------- | ---------------------------------------------------------------------- +[interface/](interface) | React based front end +[lib/framework/](lib/framework) | C++ back end for the ESP8266/ESP32 device +[src/](src) | The main.cpp and demo project to get you started +[scripts/](scripts) | Scripts that build the React interface as part of the platformio build +[platformio.ini](platformio.ini) | PlatformIO project configuration file + +### Building the firmware + +Once the platform and libraries are downloaded the back end should successfully build within PlatformIO. + +The firmware may be built by pressing the "Build" button: + +![build](/media/build.png?raw=true "build") + +Alternatively type the run command: + +```bash +platformio run +``` + +#### Uploading the firmware + +The project is configured to upload over a serial connection by default. You can change this to use OTA updates by uncommenting the relevant lines in ['platformio.ini'](platformio.ini). + +The firmware may be uploaded to the device by pressing the "Upload" button: + +![uploadfw](/media/uploadfw.png?raw=true "uploadfw") + +Alternatively run the 'upload' target: + +```bash +platformio run -t upload +``` + +### Building & uploading the interface + +The interface has been configured with create-react-app and react-app-rewired so the build can customized for the target device. The large artefacts are gzipped and source maps and service worker are excluded from the production build. This reduces the production build to around ~150k, which easily fits on the device. + +The interface will be automatically built by PlatformIO before it builds the firmware. The project can be configured to serve the interface from either PROGMEM or the filesystem as your project requires. The default configuration is to serve the content from PROGMEM, serving from the filesystem requires an additional upload step which is [documented below](#serving-the-interface-from-the-filesystem). + +#### Serving the interface from PROGMEM + +By default, the project is configured to serve the interface from PROGMEM. + +> **Tip**: You do not need to upload a file system image unless you configure the framework to [serve the interface from the filesystem](#serving-the-interface-from-the-filesystem). + +The interface will consume ~150k of program space which can be problematic if you already have a large binary artefact or if you have added large dependencies to the interface. The ESP32 binaries are fairly large in there simplest form so the addition of the interface resources requires us to use special partitioning for the ESP32. + +When building using the "node32s" profile, the project uses the custom [min_spiffs.csv](https://github.com/espressif/arduino-esp32/blob/master/tools/partitions/min_spiffs.csv) partitioning mode. You may want to disable this if you are manually uploading the file system image: + + +```ini +[env:node32s] +board_build.partitions = min_spiffs.csv +platform = espressif32 +board = node32s +``` + +#### Serving the interface from the filesystem + +If you choose to serve the interface from the filesystem you will need to change the default configuration and upload the file system image manually. + +Disable `-D PROGMEM_WWW build` flag in ['platformio.ini'](platformio.ini) and re-build the firmware. The build process will now copy the compiled interface to the `data/` directory and it may be uploaded to the device by pressing the "Upload File System image" button: + +![uploadfs](/media/uploadfs.png?raw=true "uploadfs") + +Alternatively run the 'uploadfs' target: + +```bash +platformio run -t uploadfs +``` + +### Developing the interface locally + +UI development is an iterative process so it's best to run a development server locally during interface development (using `npm start`). This can be accomplished by deploying the backend to a device and configuring the interface to point to it: + +![Development Server](/media/devserver.png?raw=true "Development Server") + +The following steps can get you up and running for local interface development: + +- [Enable CORS](#enabling-cors) in platformio.ini +- Deploy firmware to device +- [Configure endpoint root](#configuring-the-endpoint-root) with device's IP in interface/.env.development +- [Start the development server](#starting-the-development-server) with "npm start" +- Develop interface locally + +#### Enabling CORS + +You can enable CORS on the back end by uncommenting the -D ENABLE_CORS build flag in ['platformio.ini'](platformio.ini) then re-building and uploading the firmware to the device. The default settings assume you will be accessing the development server on the default port on [http://localhost:3000](http://localhost:3000) this can also be changed if required: + +```ini +-D ENABLE_CORS +-D CORS_ORIGIN=\"http://localhost:3000\" +``` + +#### Configuring the endpoint root + +The interface has a development environment which is enabled when running the development server using `npm start`. The environment file can be found in ['interface/.env.development'](interface/.env.development) and contains the HTTP root URL and the WebSocket root URL: + +```ini +REACT_APP_HTTP_ROOT=http://192.168.0.99 +REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.99 +``` + +The `REACT_APP_HTTP_ROOT` and `REACT_APP_WEB_SOCKET_ROOT` properties can be modified to point a ESP device running the back end. + +> **Tip**: You must restart the development server for changes to the environment file to come into effect. + +#### Starting the development server + +Change to the ['interface'](interface) directory with your bash shell (or Git Bash) and use the standard commands you would with any react app built with create-react-app: + +```bash +cd interface +``` + +Install the npm dependencies, if required and start the development server: + +```bash +npm install +npm start +``` +> **Tip**: You can (optionally) speed up the build by commenting out the call to build_interface.py under "extra scripts" during local development. This will prevent the npm process from building the production release every time the firmware is compiled significantly decreasing the build time. + +## Selecting features + +Many of the framework's built in features may be enabled or disabled as required at compile time. This can help save sketch space and memory if your project does not require the full suite of features. The access point and WiFi management features are "core features" and are always enabled. Feature selection may be controlled with the build flags defined in [features.ini](features.ini). + +Customize the settings as you see fit. A value of 0 will disable the specified feature: + +```ini + -D FT_PROJECT=1 + -D FT_SECURITY=1 + -D FT_MQTT=1 + -D FT_NTP=1 + -D FT_OTA=1 + -D FT_UPLOAD_FIRMWARE=1 +``` + +Flag | Description +------------------ | ---------------------------------------------- +FT_PROJECT | Controls whether the "project" section of the UI is enabled. Disable this if you don't intend to have your own screens in the UI. +FT_SECURITY | Controls whether the [security features](#security-features) are enabled. Disabling this means you won't need to authenticate to access the device and all authentication predicates will be bypassed. +FT_MQTT | Controls whether the MQTT features are enabled. Disable this if your project does not require MQTT support. +FT_NTP | Controls whether network time protocol synchronization features are enabled. Disable this if your project does not require accurate time. +FT_OTA | Controls whether OTA update support is enabled. Disable this if you won't be using the remote update feature. +FT_UPLOAD_FIRMWARE | Controls the whether the manual upload firmware feature is enabled. Disable this if you won't be manually uploading firmware. + +## Factory settings + +The framework has built-in factory settings which act as default values for the various configurable services where settings are not saved on the file system. These settings can be overridden using the build flags defined in [factory_settings.ini](factory_settings.ini). + +Customize the settings as you see fit, for example you might configure your home WiFi network as the factory default: + +```ini + -D FACTORY_WIFI_SSID=\"My Awesome WiFi Network\" + -D FACTORY_WIFI_PASSWORD=\"secret\" + -D FACTORY_WIFI_HOSTNAME=\"awesome_light_controller\" +``` + +### Default access point settings + +By default, the factory settings configure the device to bring up an access point on start up which can be used to configure the device: + +* SSID: ESP8266-React +* Password: esp-react + +### Security settings and user credentials + +By default, the factory settings configure two user accounts with the following credentials: + +Username | Password +-------- | -------- +admin | admin +guest | guest + +It is recommended that you change the user credentials from their defaults better protect your device. You can do this in the user interface, or by modifying [factory_settings.ini](factory_settings.ini) as mentioned above. + +### Customizing the factory time zone setting + +Changing factory time zone setting is a common requirement. This requires a little effort because the time zone name and POSIX format are stored as separate values for the moment. The time zone names and POSIX formats are contained in the UI code in [TZ.tsx](interface/src/ntp/TZ.tsx). Take the appropriate pair of values from there, for example, for Los Angeles you would use: + +```ini + -D FACTORY_NTP_TIME_ZONE_LABEL=\"America/Los_Angeles\" + -D FACTORY_NTP_TIME_ZONE_FORMAT=\"PST8PDT,M3.2.0,M11.1.0\" +``` + +### Placeholder substitution + +Various settings support placeholder substitution, indicated by comments in [factory_settings.ini](factory_settings.ini). This can be particularly useful where settings need to be unique, such as the Access Point SSID or MQTT client id. The following placeholders are supported: + +Placeholder | Substituted value +----------- | ----------------- +#{platform} | The microcontroller platform, e.g. "esp32" or "esp8266" +#{unique_id} | A unique identifier derived from the MAC address, e.g. "0b0a859d6816" +#{random} | A random number encoded as a hex string, e.g. "55722f94" + +You may use SettingValue::format in your own code if you require the use of these placeholders. This is demonstrated in the demo project: + +```cpp + static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) { + settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}"); + settings.name = root["name"] | SettingValue::format("light-#{unique_id}"); + settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}"); + return StateUpdateResult::CHANGED; + } +}; +``` + +## Building for different devices + +This project supports ESP8266 and ESP32 platforms. To support OTA programming, enough free space to upload the new sketch and file system image will be required. It is recommended that a board with at least 2mb of flash is used. + +The pre-configured environments are "esp12e" and "node32s". These are common ESP8266/ESP32 variants with 4mb of flash: + +![ESP12E](/media/esp12e.jpg?raw=true "ESP12E") ![ESP32](/media/esp32.jpg?raw=true "ESP32") + +The settings file ['platformio.ini'](platformio.ini) configures the supported environments. Modify these, or add new environments for the devides you need to support. The default environments are as follows: + +```ini +[env:esp12e] +platform = espressif8266 +board = esp12e +board_build.f_cpu = 160000000L + +[env:node32s] +platform = espressif32 +board = node32s +``` + +If you want to build for a different device, all you need to do is re-configure ['platformio.ini'](platformio.ini) and select an alternative environment by modifying the default_envs variable. Building for the common esp32 "node32s" board for example: + +```ini +[platformio] +;default_envs = esp12e +default_envs = node32s +``` + +## Customizing and theming + +The framework, and MaterialUI allows for a reasonable degree of customization with little effort. + +### Theming the app + +The app can be easily themed by editing the [MaterialUI theme](https://material-ui.com/customization/theming/). Edit the theme in ['interface/src/CustomMuiTheme.tsx'](interface/src/CustomMuiTheme.tsx) as you desire. For example, here is a dark theme: + +```js +const theme = createMuiTheme({ + palette: { + type:"dark", + primary: { + main: '#222', + }, + secondary: { + main: '#666', + }, + info: { + main: blueGrey[500] + }, + warning: { + main: orange[500] + }, + error: { + main: red[500] + }, + success: { + main: green[500] + } + } +}); +``` + +![Dark Theme](/media/dark.png?raw=true "Dark Theme") + +### Changing the app icon + +You can replace the app icon is located at ['interface/public/app/icon.png'](interface/public/app/icon.png) with one of your preference. A 256 x 256 PNG is recommended for best compatibility. + + +### Changing the app name + +The app name displayed on the sign in page and on the menu bar can be modified by editing the REACT_APP_NAME property in ['interface/.env'](interface/.env) + +```ini +REACT_APP_NAME=Funky IoT Project +``` + +There is also a manifest file which contains the app name to use when adding the app to a mobile device, so you may wish to also edit ['interface/public/app/manifest.json'](interface/public/app/manifest.json): + +```json +{ + "name":"Funky IoT Project", + "icons":[ + { + "src":"/app/icon.png", + "sizes":"48x48 72x72 96x96 128x128 256x256" + } + ], + "start_url":"/", + "display":"fullscreen", + "orientation":"any" +} +``` + +## Back end + +The back end is a set of REST endpoints hosted by a [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) instance. The ['lib/framework'](lib/framework) directory contains the majority of the back end code. The framework contains of a number of useful utility classes which you can use when extending it. The project also comes with a demo project to give you some help getting started. + +The framework's source is split up by feature, for example [WiFiScanner.h](lib/framework/WiFiScanner.h) implements the end points for scanning for available networks where as [WiFiSettingsService.h](lib/framework/WiFiSettingsService.h) handles configuring the WiFi settings and managing the WiFi connection. + +### Initializing the framework + +The ['src/main.cpp'](src/main.cpp) file constructs the webserver and initializes the framework. You can add endpoints to the server here to support your IoT project. The main loop is also accessable so you can run your own code easily. + +The following code creates the web server and esp8266React framework: + +```cpp +AsyncWebServer server(80); +ESP8266React esp8266React(&server); +``` + +Now in the `setup()` function the initialization is performed: + +```cpp +void setup() { + // start serial and filesystem + Serial.begin(SERIAL_BAUD_RATE); + + // start the framework and demo project + esp8266React.begin(); + + // start the server + server.begin(); +} +``` + +Finally the loop calls the framework's loop function to service the frameworks features. + +```cpp +void loop() { + // run the framework's loop function + esp8266React.loop(); +} +``` + +### Developing with the framework + +The framework promotes a modular design and exposes features you may re-use to speed up the development of your project. Where possible it is recommended that you use the features the frameworks supplies. These are documented in this section and a comprehensive example is provided by the demo project. + +The following diagram visualises how the framework's modular components fit together, each feature is described in detail below. + +![framework diagram](/media/framework.png?raw=true "framework diagram") + +#### Stateful service + +The [StatefulService.h](lib/framework/StatefulService.h) class is responsible for managing state. It has an API which allows other code to update or respond to updates in the state it manages. You can define a data class to hold state, then build a StatefulService class to manage it. After that you may attach HTTP endpoints, WebSockets or MQTT topics to the StatefulService instance to provide commonly required features. + +Here is a simple example of a state class and a StatefulService to manage it: + +```cpp +class LightState { + public: + bool on = false; + uint8_t brightness = 255; +}; + +class LightStateService : public StatefulService { +}; +``` + +You may listen for changes to state by registering an update handler callback. It is possible to remove an update handler later if required. + +```cpp +// register an update handler +update_handler_id_t myUpdateHandler = lightStateService.addUpdateHandler( + [&](const String& originId) { + Serial.print("The light's state has been updated by: "); + Serial.println(originId); + } +); + +// remove the update handler +lightStateService.removeUpdateHandler(myUpdateHandler); +``` + +An "originId" is passed to the update handler which may be used to identify the origin of an update. The default origin values the framework provides are: + +Origin | Description +--------------------- | ----------- +http | An update sent over REST (HttpEndpoint) +mqtt | An update sent over MQTT (MqttPubSub) +websocket:{clientId} | An update sent over WebSocket (WebSocketRxTx) + +StatefulService exposes a read function which you may use to safely read the state. This function takes care of protecting against parallel access to the state in multi-core enviornments such as the ESP32. + +```cpp +lightStateService.read([&](LightState& state) { + digitalWrite(LED_PIN, state.on ? HIGH : LOW); // apply the state update to the LED_PIN +}); +``` + +StatefulService also exposes an update function which allows the caller to update the state with a callback. This function automatically calls the registered update handlers if the state has been changed. The example below changes the state of the light (turns it on) using the arbitrary origin "timer" and returns the "CHANGED" state update result, indicating that a change was made: + +```cpp +lightStateService.update([&](LightState& state) { + if (state.on) { + return StateUpdateResult::UNCHANGED; // lights were already on, return UNCHANGED + } + state.on = true; // turn on the lights + return StateUpdateResult::CHANGED; // notify StatefulService by returning CHANGED +}, "timer"); +``` + +There are three possible return values for an update function which are as follows: + +Origin | Description +----------------------------- | --------------------------------------------------------------------------- +StateUpdateResult::CHANGED | The update changed the state, propagation should take place if required +StateUpdateResult::UNCHANGED | The state was unchanged, propagation should not take place +StateUpdateResult::ERROR | There was an error updating the state, propagation should not take place + +#### Serialization + +When reading or updating state from an external source (HTTP, WebSockets, or MQTT for example) the state must be marshalled into a serializable form (JSON). SettingsService provides two callback patterns which facilitate this internally: + +Callback | Signature | Purpose +---------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------- +JsonStateReader | void read(T& settings, JsonObject& root) | Reading the state object into a JsonObject +JsonStateUpdater | StateUpdateResult update(JsonObject& root, T& settings) | Updating the state from a JsonObject, returning the appropriate StateUpdateResult + + +The static functions below can be used to facilitate the serialization/deserialization of the light state: + +```cpp +class LightState { + public: + bool on = false; + uint8_t brightness = 255; + + static void read(LightState& state, JsonObject& root) { + root["on"] = state.on; + root["brightness"] = state.brightness; + } + + static StateUpdateResult update(JsonObject& root, LightState& state) { + state.on = root["on"] | false; + state.brightness = root["brightness"] | 255; + return StateUpdateResult::CHANGED; + } +}; +``` + +For convenience, the StatefulService class provides overloads of its `update` and `read` functions which utilize these functions. + +Read the state to a JsonObject using a serializer: + +```cpp +JsonObject jsonObject = jsonDocument.to(); +lightStateService->read(jsonObject, LightState::read); +``` + +Update the state from a JsonObject using a deserializer: + +```cpp +JsonObject jsonObject = jsonDocument.as(); +lightStateService->update(jsonObject, LightState::update, "timer"); +``` + +#### Endpoints + +The framework provides an [HttpEndpoint.h](lib/framework/HttpEndpoint.h) class which may be used to register GET and POST handlers to read and update the state over HTTP. You may construct an HttpEndpoint as a part of the StatefulService or separately if you prefer. + +The code below demonstrates how to extend the LightStateService class to provide an unsecured endpoint: + +```cpp +class LightStateService : public StatefulService { + public: + LightStateService(AsyncWebServer* server) : + _httpEndpoint(LightState::read, LightState::update, this, server, "/rest/lightState") { + } + + private: + HttpEndpoint _httpEndpoint; +}; +``` + +Endpoint security is provided by authentication predicates which are [documented below](#security-features). The SecurityManager and authentication predicate may be provided if a secure endpoint is required. The placeholder project shows how endpoints can be secured. + +#### Persistence + +[FSPersistence.h](lib/framework/FSPersistence.h) allows you to save state to the filesystem. FSPersistence automatically writes changes to the file system when state is updated. This feature can be disabled by calling `disableUpdateHandler()` if manual control of persistence is required. + +The code below demonstrates how to extend the LightStateService class to provide persistence: + +```cpp +class LightStateService : public StatefulService { + public: + LightStateService(FS* fs) : + _fsPersistence(LightState::read, LightState::update, this, fs, "/config/lightState.json") { + } + + private: + FSPersistence _fsPersistence; +}; +``` + +#### WebSockets + +[WebSocketTxRx.h](lib/framework/WebSocketTxRx.h) allows you to read and update state over a WebSocket connection. WebSocketTxRx automatically pushes changes to all connected clients when state is updated. + +The code below demonstrates how to extend the LightStateService class to provide an unsecured WebSocket: + +```cpp +class LightStateService : public StatefulService { + public: + LightStateService(AsyncWebServer* server) : + _webSocket(LightState::read, LightState::update, this, server, "/ws/lightState"), { + } + + private: + WebSocketTxRx _webSocket; +}; +``` + +WebSocket security is provided by authentication predicates which are [documented below](#security-features). The SecurityManager and authentication predicate may be provided if a secure WebSocket is required. The placeholder project shows how WebSockets can be secured. + +#### MQTT + +The framework includes an MQTT client which can be configured via the UI. MQTT requirements will differ from project to project so the framework exposes the client for you to use as you see fit. The framework does however provide a utility to interface StatefulService to a pair of pub/sub (state/set) topics. This utility can be used to synchronize state with software such as Home Assistant. + +[MqttPubSub.h](lib/framework/MqttPubSub.h) allows you to publish and subscribe to synchronize state over a pair of MQTT topics. MqttPubSub automatically pushes changes to the "pub" topic and reads updates from the "sub" topic. + +The code below demonstrates how to extend the LightStateService class to interface with MQTT: + +```cpp + +class LightStateService : public StatefulService { + public: + LightStateService(AsyncMqttClient* mqttClient) : + _mqttPubSub(LightState::read, + LightState::update, + this, + mqttClient, + "homeassistant/light/my_light/set", + "homeassistant/light/my_light/state") { + } + + private: + MqttPubSub _mqttPubSub; +}; +``` + +You can re-configure the pub/sub topics at runtime as required: + +```cpp +_mqttPubSub.configureBroker("homeassistant/light/desk_lamp/set", "homeassistant/light/desk_lamp/state"); +``` + +The demo project allows the user to modify the MQTT topics via the UI so they can be changed without re-flashing the firmware. + +### Security features + +The framework has security features to prevent unauthorized use of the device. This is driven by [SecurityManager.h](lib/framework/SecurityManager.h). + +On successful authentication, the /rest/signIn endpoint issues a [JSON Web Token (JWT)](https://jwt.io/) which is then sent using Bearer Authentication. The framework come with built-in predicates for verifying a users access privileges. The built in AuthenticationPredicates can be found in [SecurityManager.h](lib/framework/SecurityManager.h) and are as follows: + +Predicate | Description +-------------------- | ----------- +NONE_REQUIRED | No authentication is required. +IS_AUTHENTICATED | Any authenticated principal is permitted. +IS_ADMIN | The authenticated principal must be an admin. + +You can use the security manager to wrap any request handler function with an authentication predicate: + +```cpp +server->on("/rest/someService", HTTP_GET, + _securityManager->wrapRequest(std::bind(&SomeService::someService, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED) +); +``` + +### Accessing settings and services + +The framework supplies access to various features via getter functions: + +SettingsService | Description +---------------------------- | ---------------------------------------------- +getFS() | The filesystem used by the framework +getSecurityManager() | The security manager - detailed above +getSecuritySettingsService() | Configures the users and other security settings +getWiFiSettingsService() | Configures and manages the WiFi network connection +getAPSettingsService() | Configures and manages the Access Point +getNTPSettingsService() | Configures and manages the network time +getOTASettingsService() | Configures and manages the Over-The-Air update feature +getMqttSettingsService() | Configures and manages the MQTT connection +getMqttClient() | Provides direct access to the MQTT client instance + +The core features use the [StatefulService.h](lib/framework/StatefulService.h) class and can therefore you can change settings or observe changes to settings through the read/update API. + +Inspect the current WiFi settings: + +```cpp +esp8266React.getWiFiSettingsService()->read([&](WiFiSettings& wifiSettings) { + Serial.print("The ssid is:"); + Serial.println(wifiSettings.ssid); +}); +``` + +Configure the WiFi SSID and password manually: + +```cpp +esp8266React.getWiFiSettingsService()->update([&](WiFiSettings& wifiSettings) { + wifiSettings.ssid = "MyNetworkSSID"; + wifiSettings.password = "MySuperSecretPassword"; + return StateUpdateResult::CHANGED; +}, "myapp"); +``` + +Observe changes to the WiFiSettings: + +```cpp +esp8266React.getWiFiSettingsService()->addUpdateHandler( + [&](const String& originId) { + Serial.println("The WiFi Settings were updated!"); + } +); +``` + +## Libraries Used + +* [React](https://reactjs.org/) +* [Material-UI](https://material-ui.com/) +* [notistack](https://github.com/iamhosseindhv/notistack) +* [ArduinoJson](https://github.com/bblanchon/ArduinoJson) +* [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) +* [AsyncMqttClient](https://github.com/marvinroger/async-mqtt-client) diff --git a/factory_settings.ini b/factory_settings.ini new file mode 100644 index 0000000..b9fbd60 --- /dev/null +++ b/factory_settings.ini @@ -0,0 +1,54 @@ +; The indicated settings support placeholder substitution as follows: +; +; #{platform} - The microcontroller platform, e.g. "esp32" or "esp8266" +; #{unique_id} - A unique identifier derived from the MAC address, e.g. "0b0a859d6816" +; #{random} - A random number encoded as a hex string, e.g. "55722f94" + +[factory_settings] +build_flags = + ; WiFi settings + -D FACTORY_WIFI_SSID=\"\" + -D FACTORY_WIFI_PASSWORD=\"\" + -D FACTORY_WIFI_HOSTNAME=\"#{platform}-#{unique_id}\" ; supports placeholders + + ; Access point settings + -D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED + -D FACTORY_AP_SSID=\"ESP8266-React-#{unique_id}\" ; 1-64 characters, supports placeholders + -D FACTORY_AP_PASSWORD=\"esp-react\" ; 8-64 characters + -D FACTORY_AP_CHANNEL=1 + -D FACTORY_AP_SSID_HIDDEN=false + -D FACTORY_AP_MAX_CLIENTS=4 + -D FACTORY_AP_LOCAL_IP=\"192.168.4.1\" + -D FACTORY_AP_GATEWAY_IP=\"192.168.4.1\" + -D FACTORY_AP_SUBNET_MASK=\"255.255.255.0\" + + ; User credentials for admin and guest user + -D FACTORY_ADMIN_USERNAME=\"admin\" + -D FACTORY_ADMIN_PASSWORD=\"admin\" + -D FACTORY_GUEST_USERNAME=\"guest\" + -D FACTORY_GUEST_PASSWORD=\"guest\" + + ; NTP settings + -D FACTORY_NTP_ENABLED=true + -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/London\" + -D FACTORY_NTP_TIME_ZONE_FORMAT=\"GMT0BST,M3.5.0/1,M10.5.0\" + -D FACTORY_NTP_SERVER=\"time.google.com\" + + ; OTA settings + -D FACTORY_OTA_PORT=8266 + -D FACTORY_OTA_PASSWORD=\"esp-react\" + -D FACTORY_OTA_ENABLED=true + + ; MQTT settings + -D FACTORY_MQTT_ENABLED=false + -D FACTORY_MQTT_HOST=\"test.mosquitto.org\" + -D FACTORY_MQTT_PORT=1883 + -D FACTORY_MQTT_USERNAME=\"\" ; supports placeholders + -D FACTORY_MQTT_PASSWORD=\"\" + -D FACTORY_MQTT_CLIENT_ID=\"#{platform}-#{unique_id}\" ; supports placeholders + -D FACTORY_MQTT_KEEP_ALIVE=60 + -D FACTORY_MQTT_CLEAN_SESSION=true + -D FACTORY_MQTT_MAX_TOPIC_LENGTH=128 + + ; JWT Secret + -D FACTORY_JWT_SECRET=\"#{random}-#{random}\" ; supports placeholders diff --git a/features.ini b/features.ini new file mode 100644 index 0000000..ffb890d --- /dev/null +++ b/features.ini @@ -0,0 +1,8 @@ +[features] +build_flags = + -D FT_PROJECT=1 + -D FT_SECURITY=1 + -D FT_MQTT=1 + -D FT_NTP=1 + -D FT_OTA=1 + -D FT_UPLOAD_FIRMWARE=1 diff --git a/interface/.env b/interface/.env new file mode 100644 index 0000000..a312b2a --- /dev/null +++ b/interface/.env @@ -0,0 +1,5 @@ +# This is the name of your project. It appears on the sign-in page and in the menu bar. +REACT_APP_PROJECT_NAME=ESP8266 React + +# This is the url path your project will be exposed under. +REACT_APP_PROJECT_PATH=project diff --git a/interface/.env.development b/interface/.env.development new file mode 100644 index 0000000..b12cfd0 --- /dev/null +++ b/interface/.env.development @@ -0,0 +1,4 @@ +# Change the IP address to that of your ESP device to enable local development of the UI. +# Remember to also enable CORS in platformio.ini before uploading the code to the device. +REACT_APP_HTTP_ROOT=http://192.168.0.88 +REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.88 diff --git a/interface/.env.production b/interface/.env.production new file mode 100644 index 0000000..ba7cc18 --- /dev/null +++ b/interface/.env.production @@ -0,0 +1 @@ +GENERATE_SOURCEMAP=false diff --git a/interface/config-overrides.js b/interface/config-overrides.js new file mode 100644 index 0000000..61cd113 --- /dev/null +++ b/interface/config-overrides.js @@ -0,0 +1,37 @@ +const ManifestPlugin = require('webpack-manifest-plugin'); +const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const CompressionPlugin = require('compression-webpack-plugin'); +const ProgmemGenerator = require('./progmem-generator.js'); + +const path = require('path'); +const fs = require('fs'); + +module.exports = function override(config, env) { + if (env === "production") { + // rename the ouput file, we need it's path to be short, for embedded FS + config.output.filename = 'js/[id].[chunkhash:4].js'; + config.output.chunkFilename = 'js/[id].[chunkhash:4].js'; + + // take out the manifest and service worker plugins + config.plugins = config.plugins.filter(plugin => !(plugin instanceof ManifestPlugin)); + config.plugins = config.plugins.filter(plugin => !(plugin instanceof WorkboxWebpackPlugin.GenerateSW)); + + // shorten css filenames + const miniCssExtractPlugin = config.plugins.find((plugin) => plugin instanceof MiniCssExtractPlugin); + miniCssExtractPlugin.options.filename = "css/[id].[contenthash:4].css"; + miniCssExtractPlugin.options.chunkFilename = "css/[id].[contenthash:4].c.css"; + + // build progmem data files + config.plugins.push(new ProgmemGenerator({ outputPath: "../lib/framework/WWWData.h", bytesPerLine: 20 })); + + // add compression plugin, compress javascript + config.plugins.push(new CompressionPlugin({ + filename: "[path].gz[query]", + algorithm: "gzip", + test: /\.(js)$/, + deleteOriginalAssets: true + })); + } + return config; +} diff --git a/interface/package-lock.json b/interface/package-lock.json new file mode 100644 index 0000000..014040e --- /dev/null +++ b/interface/package-lock.json @@ -0,0 +1,14652 @@ +{ + "name": "esp8266-react", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz", + "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==" + }, + "@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz", + "integrity": "sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-react-jsx-experimental": { + "version": "7.12.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz", + "integrity": "sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "requires": { + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", + "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==" + }, + "@babel/helper-wrap-function": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==" + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz", + "integrity": "sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", + "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz", + "integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz", + "integrity": "sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz", + "integrity": "sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-flow": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "requires": { + "@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": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz", + "integrity": "sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.5.tgz", + "integrity": "sha512-2xkcPqqrYiOQgSlM/iwto1paPijjsDbUynN13tI6bosDz/jOW3CRzYguIE8wKX32h+msbBM22Dv5fwrFkUOZjQ==", + "requires": { + "@babel/helper-builder-react-jsx": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.5.tgz", + "integrity": "sha512-1JJusg3iPgsZDthyWiCr3KQiGs31ikU/mSf2N2dSYEAO0GEImmVUbWf0VoSDGDFTAn5Dj4DUiR6SdIXHY7tELA==", + "requires": { + "@babel/helper-builder-react-jsx-experimental": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz", + "integrity": "sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz", + "integrity": "sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz", + "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==", + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", + "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz", + "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-typescript": "^7.12.1" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", + "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", + "requires": { + "@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" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.5.tgz", + "integrity": "sha512-jcs++VPrgyFehkMezHtezS2BpnUlR7tQFAyesJn1vGTO9aTFZrgIQrA5YydlTwxbcjMwkFY6i04flCigRRr3GA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.5", + "@babel/plugin-transform-react-jsx-development": "^7.12.5", + "@babel/plugin-transform-react-jsx-self": "^7.12.1", + "@babel/plugin-transform-react-jsx-source": "^7.12.1", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + } + }, + "@babel/preset-typescript": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz", + "integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-typescript": "^7.9.0" + } + }, + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" + }, + "@csstools/normalize.css": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", + "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==" + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@material-ui/core": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz", + "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.10.0", + "@material-ui/system": "^4.9.14", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.10.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/icons": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz", + "integrity": "sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==", + "requires": { + "@babel/runtime": "^7.4.4" + } + }, + "@material-ui/styles": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", + "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.9.6", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.9.14", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz", + "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.9.6", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" + }, + "@material-ui/utils": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz", + "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "requires": { + "mkdirp": "^1.0.4" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz", + "integrity": "sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz", + "integrity": "sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz", + "integrity": "sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz", + "integrity": "sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz", + "integrity": "sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz", + "integrity": "sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==" + }, + "@svgr/babel-preset": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.3.tgz", + "integrity": "sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^4.2.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^4.2.0", + "@svgr/babel-plugin-svg-dynamic-title": "^4.3.3", + "@svgr/babel-plugin-svg-em-dimensions": "^4.2.0", + "@svgr/babel-plugin-transform-react-native-svg": "^4.2.0", + "@svgr/babel-plugin-transform-svg-component": "^4.2.0" + } + }, + "@svgr/core": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.3.tgz", + "integrity": "sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w==", + "requires": { + "@svgr/plugin-jsx": "^4.3.3", + "camelcase": "^5.3.1", + "cosmiconfig": "^5.2.1" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz", + "integrity": "sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg==", + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@svgr/plugin-jsx": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz", + "integrity": "sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w==", + "requires": { + "@babel/core": "^7.4.5", + "@svgr/babel-preset": "^4.3.3", + "@svgr/hast-util-to-babel-ast": "^4.3.2", + "svg-parser": "^2.0.0" + } + }, + "@svgr/plugin-svgo": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz", + "integrity": "sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==", + "requires": { + "cosmiconfig": "^5.2.1", + "merge-deep": "^3.0.2", + "svgo": "^1.2.2" + } + }, + "@svgr/webpack": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.3.3.tgz", + "integrity": "sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg==", + "requires": { + "@babel/core": "^7.4.5", + "@babel/plugin-transform-react-constant-elements": "^7.0.0", + "@babel/preset-env": "^7.4.5", + "@babel/preset-react": "^7.0.0", + "@svgr/core": "^4.3.3", + "@svgr/plugin-jsx": "^4.3.3", + "@svgr/plugin-svgo": "^4.3.1", + "loader-utils": "^1.2.3" + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", + "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==" + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==" + }, + "@types/jwt-decode": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-3.1.0.tgz", + "integrity": "sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w==", + "requires": { + "jwt-decode": "*" + } + }, + "@types/lodash": { + "version": "4.14.165", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz", + "integrity": "sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==" + }, + "@types/material-ui": { + "version": "0.21.8", + "resolved": "https://registry.npmjs.org/@types/material-ui/-/material-ui-0.21.8.tgz", + "integrity": "sha512-Rsx3tRNoYkidDKfMfh+cegtOHMl73akzKnQ5pzxTrbx5oaUXLtG6YVlvtS43uebOSTDf8GQNaseB52r3zVagEg==", + "requires": { + "@types/react": "*", + "@types/react-addons-linked-state-mixin": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, + "@types/node": { + "version": "12.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.4.tgz", + "integrity": "sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "@types/react": { + "version": "16.9.56", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz", + "integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + } + } + }, + "@types/react-addons-linked-state-mixin": { + "version": "0.14.21", + "resolved": "https://registry.npmjs.org/@types/react-addons-linked-state-mixin/-/react-addons-linked-state-mixin-0.14.21.tgz", + "integrity": "sha512-3UF7Szd3JyuU+z90kqu8L4VdDWp7SUC0eRjV2QmMEliaHODGLi5XyO5ctS50K/lG6fjC0dSAPVbvnqv0nPoGMQ==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-dom": { + "version": "16.9.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz", + "integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-material-ui-form-validator": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/react-material-ui-form-validator/-/react-material-ui-form-validator-2.1.0.tgz", + "integrity": "sha512-izwjulCvMsN01H8oF8X1IN7QDMHeaGmjYoAxL/cmlUJLtFH0BLLUNmlmZERrjNM+MOJAXUaOkwoCqOHlCtqCzQ==", + "requires": { + "@types/material-ui": "*", + "@types/react": "*" + } + }, + "@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "requires": { + "@types/react": "*" + } + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "@types/yargs": { + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", + "integrity": "sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", + "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", + "requires": { + "@typescript-eslint/experimental-utils": "2.34.0", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", + "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", + "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.34.0", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", + "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + } + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + } + } + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + }, + "adjust-sourcemap-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", + "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==" + }, + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "requires": { + "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" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "requires": { + "@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-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "requires": { + "babylon": "^6.18.0" + } + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + } + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz", + "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==" + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "babel-preset-react-app": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.1.2.tgz", + "integrity": "sha512-k58RtQOKH21NyKtzptoAvtAODuAJJs3ZhqBMl456/GnXEQ/0La92pNmwgWoMn5pBTrsvk3YYXdY7zpY4e3UIxA==", + "requires": { + "@babel/core": "7.9.0", + "@babel/plugin-proposal-class-properties": "7.8.3", + "@babel/plugin-proposal-decorators": "7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.8.3", + "@babel/plugin-proposal-numeric-separator": "7.8.3", + "@babel/plugin-proposal-optional-chaining": "7.9.0", + "@babel/plugin-transform-flow-strip-types": "7.9.0", + "@babel/plugin-transform-react-display-name": "7.8.3", + "@babel/plugin-transform-runtime": "7.9.0", + "@babel/preset-env": "7.9.0", + "@babel/preset-react": "7.9.1", + "@babel/preset-typescript": "7.9.0", + "@babel/runtime": "7.9.0", + "babel-plugin-macros": "2.8.0", + "babel-plugin-transform-react-remove-prop-types": "0.4.24" + }, + "dependencies": { + "@babel/plugin-proposal-class-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz", + "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/preset-env": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.0.tgz", + "integrity": "sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==", + "requires": { + "@babel/compat-data": "^7.9.0", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-numeric-separator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.9.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@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.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.9.0", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.9.0", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.9.0", + "@babel/plugin-transform-modules-commonjs": "^7.9.0", + "@babel/plugin-transform-modules-systemjs": "^7.9.0", + "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.7", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.9.0", + "browserslist": "^4.9.1", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-react": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.1.tgz", + "integrity": "sha512-aJBYF23MPj0RNdp/4bHnAP0NVqqZRr9kl0NAOP4nJCex6OYVio59+dnQzsAWFuogdLyeaKA1hmfUIVZkY5J+TQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-transform-react-display-name": "^7.8.3", + "@babel/plugin-transform-react-jsx": "^7.9.1", + "@babel/plugin-transform-react-jsx-development": "^7.9.0", + "@babel/plugin-transform-react-jsx-self": "^7.9.0", + "@babel/plugin-transform-react-jsx-source": "^7.9.0" + } + }, + "@babel/runtime": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.0.tgz", + "integrity": "sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "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": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "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-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", + "requires": { + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + }, + "camel-case": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "requires": { + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001157", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz", + "integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "requires": { + "rsvp": "^4.8.4" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", + "requires": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + } + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", + "requires": { + "arity-n": "^1.0.4" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "compression-webpack-plugin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-4.0.1.tgz", + "integrity": "sha512-0mg6PgwTsUe5LEcUrOu3ob32vraDx2VdbMGAT1PARcOV+UJWDYZFdkSo6RbHoGQ061mmmkC7XpRKOlvwm/gzJQ==", + "requires": { + "cacache": "^15.0.5", + "find-cache-dir": "^3.3.1", + "schema-utils": "^2.7.0", + "serialize-javascript": "^4.0.0", + "webpack-sources": "^1.4.3" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==" + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-js": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz", + "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA==" + }, + "core-js-compat": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz", + "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==", + "requires": { + "browserslist": "^4.14.6", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-js-pure": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.7.0.tgz", + "integrity": "sha512-EZD2ckZysv8MMt4J6HSvS9K2GdtlZtdBncKAmF9lr2n0c9dJUaUN88PSTjvgwCgQPWKTkERXITgS6JJRAnljtg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "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": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "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" + } + }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "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" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "css-loader": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", + "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.23", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.1.1", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.2", + "schema-utils": "^2.6.0" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=" + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=" + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" + }, + "csso": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.1.0.tgz", + "integrity": "sha512-h+6w/W1WqXaJA4tb1dk7r5tVbOm97MsKxzwnvOR04UQ6GILroryjMWu3pmCCtL2mLaEStQ0fZgeGiy99mo7iyg==", + "requires": { + "css-tree": "^1.0.0" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0.tgz", + "integrity": "sha512-CdVYz/Yuqw0VdKhXPBIgi8DO3NicJVYZNWeX9XcIuSp9ZoFT5IcleVRW07O5rMjdcx1mb+MEJPknTTEW7DdsYw==", + "requires": { + "mdn-data": "2.0.12", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.12.tgz", + "integrity": "sha512-ULbAlgzVb8IqZ0Hsxm6hHSlQl3Jckst2YEQS7fODu9ilNWy2LvcoSY7TRFIktABP2mdppBioc66va90T+NUs8Q==" + } + } + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "requires": { + "cssom": "0.3.x" + } + }, + "csstype": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", + "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + } + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", + "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", + "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.592", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.592.tgz", + "integrity": "sha512-kGNowksvqQiPb1pUSQKpd8JFoGPLxYOwduNRCqCxGh/2Q1qE2JdmwouCW41lUzDxOb/2RIV4lR0tVIfboWlO9A==" + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "requires": { + "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" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "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", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + } + } + }, + "eslint-config-react-app": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz", + "integrity": "sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ==", + "requires": { + "confusing-browser-globals": "^1.0.9" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "eslint-loader": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-3.0.3.tgz", + "integrity": "sha512-+YRqB95PnNvxNp1HEjQmvf9KNvCin5HXYYseOXVC2U0KEcw4IkQ2IQEBG46j7+gW39bMzeu0GsUhVbBY3Votpw==", + "requires": { + "fs-extra": "^8.1.0", + "loader-fs-cache": "^1.0.2", + "loader-utils": "^1.2.3", + "object-hash": "^2.0.1", + "schema-utils": "^2.6.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz", + "integrity": "sha512-W5hLjpFfZyZsXfo5anlu7HM970JBDqbEshAJUkeczP6BFCIfJXuiIBQXyberLRtOStT0OGPF8efeTbxlHk4LpQ==", + "requires": { + "lodash": "^4.17.15" + } + }, + "eslint-plugin-import": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", + "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", + "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "requires": { + "@babel/runtime": "^7.4.5", + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.1" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + } + } + }, + "eslint-plugin-react": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz", + "integrity": "sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==", + "requires": { + "array-includes": "^3.1.1", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.3", + "object.entries": "^1.1.1", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.15.1", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.2", + "xregexp": "^4.3.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz", + "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==" + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "requires": { + "bser": "2.1.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, + "file-selector": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.3.tgz", + "integrity": "sha512-d+hc9ctodLSVG55V2V5I4/eJBEr2p2na/kDN46Ty7PBhdp/Q5NmeQTXKa1Hx3AcIL1lgSFKZI0ve/v5ZXGCDkQ==", + "requires": { + "tslib": "^2.0.3" + } + }, + "filesize": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", + "integrity": "sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg==" + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "fork-ts-checker-webpack-plugin": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz", + "integrity": "sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ==", + "requires": { + "babel-code-frame": "^6.22.0", + "chalk": "^2.4.1", + "chokidar": "^3.3.0", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "requires": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "dependencies": { + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "harmony-reflect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz", + "integrity": "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + } + } + }, + "html-webpack-plugin": { + "version": "4.0.0-beta.11", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", + "integrity": "sha512-4Xzepf0qWxf8CGg7/WQM5qBB2Lc/NFI7MhU59eUDTkuQp3skZczH4UA1d6oQyDEIoMDgERVhRyTdtUPZ5s5HBg==", + "requires": { + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "dependencies": { + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "requires": { + "postcss": "^7.0.14" + } + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "immer": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", + "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==" + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "requires": { + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "requires": { + "html-escaper": "^2.0.0" + } + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-jsdom-fourteen": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-1.0.1.tgz", + "integrity": "sha512-DojMX1sY+at5Ep+O9yME34CdidZnO3/zfPh8UW+918C5fIZET5vCjfkegixmsi7AtdYfkr4bPlIzmWnlvQkP7Q==", + "requires": { + "@jest/environment": "^24.3.0", + "@jest/fake-timers": "^24.3.0", + "@jest/types": "^24.3.0", + "jest-mock": "^24.0.0", + "jest-util": "^24.0.0", + "jsdom": "^14.1.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==" + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true + } + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==" + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==" + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-watch-typeahead": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz", + "integrity": "sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.1", + "jest-regex-util": "^24.9.0", + "jest-watcher": "^24.3.0", + "slash": "^3.0.0", + "string-length": "^3.1.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "string-length": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^5.2.0" + } + } + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + } + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jss": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.4.0.tgz", + "integrity": "sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + } + } + }, + "jss-plugin-camel-case": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz", + "integrity": "sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.4.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz", + "integrity": "sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-global": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz", + "integrity": "sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-nested": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz", + "integrity": "sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz", + "integrity": "sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz", + "integrity": "sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz", + "integrity": "sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.4.0" + } + }, + "jsx-ast-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "jwt-decode": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.1.tgz", + "integrity": "sha512-EaH9dTD9ogCmxZRdiTsIUIJslqjoFfk8nEAi+Bq8u/aUjrVuhZ6eZjhWRH6SC9NBA0Lwr3K35H2zDnWvK/n4vQ==" + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "requires": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "requires": { + "find-up": "^1.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "loglevel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", + "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "requires": { + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "merge-deep": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", + "integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==", + "requires": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "requires": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + } + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "node-releases": { + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz", + "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "notistack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-1.0.1.tgz", + "integrity": "sha512-2T1WkokzRCM8N9EdueaXja160IMFIMHVhRu0fGkDje7qCzwBHlTMZY2NULQzB2GFOO6iGVzl5GCX2XrJIzI8bw==", + "requires": { + "clsx": "^1.1.0", + "hoist-non-react-statics": "^3.3.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-hash": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", + "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", + "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-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" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + } + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimize-css-assets-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==", + "requires": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=" + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "param-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", + "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", + "requires": { + "dot-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "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-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", + "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "requires": { + "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": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-browser-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz", + "integrity": "sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig==", + "requires": { + "postcss": "^7" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz", + "integrity": "sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-font-variant": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", + "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-initial": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz", + "integrity": "sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==", + "requires": { + "lodash.template": "^4.5.0", + "postcss": "^7.0.2" + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-normalize": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-8.0.1.tgz", + "integrity": "sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ==", + "requires": { + "@csstools/normalize.css": "^10.1.0", + "browserslist": "^4.6.2", + "postcss": "^7.0.17", + "postcss-browser-comments": "^3.0.0", + "sanitize.css": "^10.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-safe-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", + "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-not": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz", + "integrity": "sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "pretty-bytes": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==" + }, + "pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "requires": { + "asap": "~2.0.6" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-polyfill": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.0.tgz", + "integrity": "sha512-OzSf6gcCUQ01byV4BgwyUCswlaQQ6gzXc23aLQWhicvfX9kfsUiUhgt3CCQej8jDnl8/PhGF31JdHX2/MzF3WA==" + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "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" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + } + } + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-app-polyfill": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz", + "integrity": "sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g==", + "requires": { + "core-js": "^3.5.0", + "object-assign": "^4.1.1", + "promise": "^8.0.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.3", + "whatwg-fetch": "^3.0.0" + } + }, + "react-app-rewired": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.1.6.tgz", + "integrity": "sha512-06flj0kK5tf/RN4naRv/sn6j3sQd7rsURoRLKLpffXDzJeNiAaTNic+0I8Basojy5WDwREkTqrMLewSAjcb13w==", + "dev": true, + "requires": { + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "react-dev-utils": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz", + "integrity": "sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ==", + "requires": { + "@babel/code-frame": "7.8.3", + "address": "1.1.2", + "browserslist": "4.10.0", + "chalk": "2.4.2", + "cross-spawn": "7.0.1", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "2.0.0", + "filesize": "6.0.1", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "3.1.1", + "global-modules": "2.0.0", + "globby": "8.0.2", + "gzip-size": "5.1.1", + "immer": "1.10.0", + "inquirer": "7.0.4", + "is-root": "2.1.0", + "loader-utils": "1.2.3", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "react-error-overlay": "^6.0.7", + "recursive-readdir": "2.2.2", + "shell-quote": "1.7.2", + "strip-ansi": "6.0.0", + "text-table": "0.2.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "browserslist": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz", + "integrity": "sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA==", + "requires": { + "caniuse-lite": "^1.0.30001035", + "electron-to-chromium": "^1.3.378", + "node-releases": "^1.1.52", + "pkg-up": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" + }, + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + }, + "inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + } + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-dropzone": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.2.4.tgz", + "integrity": "sha512-EGSvK2CxFTuc28WxwuJCICyuYFX8b+sRumwU6Bs6sTbElV2HtQkT0d6C+HEee6XfbjiLIZ+Th9uji27rvo2wGw==", + "requires": { + "attr-accept": "^2.2.1", + "file-selector": "^0.2.2", + "prop-types": "^15.7.2" + } + }, + "react-error-overlay": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", + "integrity": "sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==" + }, + "react-form-validator-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-form-validator-core/-/react-form-validator-core-1.0.0.tgz", + "integrity": "sha512-J+itG0oD/6VikXWJlOyjpoe3QaB35iX2WeFuQ1psxyJ5KkjMncBcOjUw/lemW8dYCOC7vQTFGUjlvwkGenTgig==", + "requires": { + "create-react-context": "^0.3.0", + "promise-polyfill": "8.1.0", + "prop-types": "^15.0.0", + "react-lifecycles-compat": "^3.0.2" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-material-ui-form-validator": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-material-ui-form-validator/-/react-material-ui-form-validator-2.1.1.tgz", + "integrity": "sha512-4+W6dbRVyfUnWPHaBWJTLo6LTyim/ZyCWre/GUuBalbH0l4KMlOO7VUcz+h9Vy4cTQT1/TQIxzdfitD5Y+nYMw==", + "requires": { + "prop-types": "^15.0.0", + "react-form-validator-core": "1.0.0" + } + }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-scripts": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz", + "integrity": "sha512-7J7GZyF/QvZkKAZLneiOIhHozvOMHey7hO9cdO9u68jjhGZlI8hDdOm6UyuHofn6Ajc9Uji5I6Psm/nKNuWdyw==", + "requires": { + "@babel/core": "7.9.0", + "@svgr/webpack": "4.3.3", + "@typescript-eslint/eslint-plugin": "^2.10.0", + "@typescript-eslint/parser": "^2.10.0", + "babel-eslint": "10.1.0", + "babel-jest": "^24.9.0", + "babel-loader": "8.1.0", + "babel-plugin-named-asset-import": "^0.3.6", + "babel-preset-react-app": "^9.1.2", + "camelcase": "^5.3.1", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "css-loader": "3.4.2", + "dotenv": "8.2.0", + "dotenv-expand": "5.1.0", + "eslint": "^6.6.0", + "eslint-config-react-app": "^5.2.1", + "eslint-loader": "3.0.3", + "eslint-plugin-flowtype": "4.6.0", + "eslint-plugin-import": "2.20.1", + "eslint-plugin-jsx-a11y": "6.2.3", + "eslint-plugin-react": "7.19.0", + "eslint-plugin-react-hooks": "^1.6.1", + "file-loader": "4.3.0", + "fs-extra": "^8.1.0", + "fsevents": "2.1.2", + "html-webpack-plugin": "4.0.0-beta.11", + "identity-obj-proxy": "3.0.0", + "jest": "24.9.0", + "jest-environment-jsdom-fourteen": "1.0.1", + "jest-resolve": "24.9.0", + "jest-watch-typeahead": "0.4.2", + "mini-css-extract-plugin": "0.9.0", + "optimize-css-assets-webpack-plugin": "5.0.3", + "pnp-webpack-plugin": "1.6.4", + "postcss-flexbugs-fixes": "4.1.0", + "postcss-loader": "3.0.0", + "postcss-normalize": "8.0.1", + "postcss-preset-env": "6.7.0", + "postcss-safe-parser": "4.0.1", + "react-app-polyfill": "^1.0.6", + "react-dev-utils": "^10.2.1", + "resolve": "1.15.0", + "resolve-url-loader": "3.1.2", + "sass-loader": "8.0.2", + "semver": "6.3.0", + "style-loader": "0.23.1", + "terser-webpack-plugin": "2.3.8", + "ts-pnp": "1.1.6", + "url-loader": "2.3.0", + "webpack": "4.42.0", + "webpack-dev-server": "3.11.0", + "webpack-manifest-plugin": "2.2.0", + "workbox-webpack-plugin": "4.3.1" + } + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "requires": { + "util.promisify": "^1.0.0" + } + }, + "recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "requires": { + "minimatch": "3.0.4" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" + }, + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "requires": { + "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": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "renderkid": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.4.tgz", + "integrity": "sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==", + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.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" + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "resolve-url-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", + "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", + "requires": { + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.21", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=" + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=" + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==" + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sanitize.css": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-10.0.0.tgz", + "integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg==" + }, + "sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "dependencies": { + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "requires": { + "xmlchars": "^2.1.1" + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "requires": { + "is-buffer": "^1.0.2" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=" + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-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" + } + } + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + } + }, + "sockette": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/sockette/-/sockette-2.0.6.tgz", + "integrity": "sha512-W6iG8RGV6Zife3Cj+FhuyHV447E6fqFM2hKmnaQrTvg3OydINV3Msj3WPFbX76blUlUxvQSMMMdrJxce8NqI5Q==" + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.matchall": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz", + "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-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" + } + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-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" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "requires": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "terser-webpack-plugin": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", + "integrity": "sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w==", + "requires": { + "cacache": "^13.0.1", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.6.12", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "cacache": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", + "integrity": "sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w==", + "requires": { + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "p-map": "^3.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^2.7.1", + "ssri": "^7.0.0", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ssri": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", + "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "requires": { + "figgy-pudding": "^3.5.1", + "minipass": "^3.1.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "ts-pnp": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz", + "integrity": "sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ==" + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==" + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "requires": { + "makeerror": "1.0.x" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "webpack": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", + "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-manifest-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", + "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "requires": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "object.entries": "^1.1.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz", + "integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "workbox-background-sync": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz", + "integrity": "sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-broadcast-update": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz", + "integrity": "sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-build": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.3.1.tgz", + "integrity": "sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw==", + "requires": { + "@babel/runtime": "^7.3.4", + "@hapi/joi": "^15.0.0", + "common-tags": "^1.8.0", + "fs-extra": "^4.0.2", + "glob": "^7.1.3", + "lodash.template": "^4.4.0", + "pretty-bytes": "^5.1.0", + "stringify-object": "^3.3.0", + "strip-comments": "^1.0.2", + "workbox-background-sync": "^4.3.1", + "workbox-broadcast-update": "^4.3.1", + "workbox-cacheable-response": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-expiration": "^4.3.1", + "workbox-google-analytics": "^4.3.1", + "workbox-navigation-preload": "^4.3.1", + "workbox-precaching": "^4.3.1", + "workbox-range-requests": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1", + "workbox-streams": "^4.3.1", + "workbox-sw": "^4.3.1", + "workbox-window": "^4.3.1" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "workbox-cacheable-response": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz", + "integrity": "sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.3.1.tgz", + "integrity": "sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg==" + }, + "workbox-expiration": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.3.1.tgz", + "integrity": "sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-google-analytics": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz", + "integrity": "sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg==", + "requires": { + "workbox-background-sync": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1" + } + }, + "workbox-navigation-preload": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz", + "integrity": "sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-precaching": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.3.1.tgz", + "integrity": "sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-range-requests": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz", + "integrity": "sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-routing": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.3.1.tgz", + "integrity": "sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-strategies": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.3.1.tgz", + "integrity": "sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-streams": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.3.1.tgz", + "integrity": "sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-sw": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.3.1.tgz", + "integrity": "sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w==" + }, + "workbox-webpack-plugin": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.3.1.tgz", + "integrity": "sha512-gJ9jd8Mb8wHLbRz9ZvGN57IAmknOipD3W4XNE/Lk/4lqs5Htw4WOQgakQy/o/4CoXQlMCYldaqUg+EJ35l9MEQ==", + "requires": { + "@babel/runtime": "^7.0.0", + "json-stable-stringify": "^1.0.1", + "workbox-build": "^4.3.1" + } + }, + "workbox-window": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.3.1.tgz", + "integrity": "sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "requires": { + "microevent.ts": "~0.1.1" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xregexp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.0.tgz", + "integrity": "sha512-83y4aa8o8o4NZe+L+46wpa+F1cWR/wCGOWI3tzqUso0w3/KAvXy0+Di7Oe/cbNMixDR4Jmi7NEybWU6ps25Wkg==", + "requires": { + "@babel/runtime-corejs3": "^7.12.1" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "zlib": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz", + "integrity": "sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA=" + } + } +} diff --git a/interface/package.json b/interface/package.json new file mode 100644 index 0000000..b4080e1 --- /dev/null +++ b/interface/package.json @@ -0,0 +1,57 @@ +{ + "name": "esp8266-react", + "version": "0.1.0", + "private": true, + "dependencies": { + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@types/jwt-decode": "^3.1.0", + "@types/lodash": "^4.14.165", + "@types/node": "^12.12.32", + "@types/react": "^16.9.56", + "@types/react-dom": "^16.9.9", + "@types/react-material-ui-form-validator": "^2.1.0", + "@types/react-router": "^5.1.8", + "@types/react-router-dom": "^5.1.6", + "compression-webpack-plugin": "^4.0.0", + "jwt-decode": "^3.1.1", + "lodash": "^4.17.20", + "mime-types": "^2.1.27", + "moment": "^2.29.1", + "notistack": "^1.0.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", + "react-dropzone": "^11.2.4", + "react-form-validator-core": "^1.0.0", + "react-material-ui-form-validator": "^2.1.1", + "react-router": "^5.2.0", + "react-router-dom": "^5.2.0", + "react-scripts": "3.4.4", + "sockette": "^2.0.6", + "typescript": "^4.0.2", + "zlib": "^1.0.5" + }, + "scripts": { + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "react-app-rewired": "^2.1.6" + } +} diff --git a/interface/progmem-generator.js b/interface/progmem-generator.js new file mode 100644 index 0000000..674adae --- /dev/null +++ b/interface/progmem-generator.js @@ -0,0 +1,122 @@ +const { resolve, relative, sep } = require('path'); +const { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } = require('fs'); +var zlib = require('zlib'); +var mime = require('mime-types'); + +const ARDUINO_INCLUDES = "#include \n\n"; + +function getFilesSync(dir, files = []) { + readdirSync(dir, { withFileTypes: true }).forEach(entry => { + const entryPath = resolve(dir, entry.name); + if (entry.isDirectory()) { + getFilesSync(entryPath, files); + } else { + files.push(entryPath); + } + }) + return files; +} + +function coherseToBuffer(input) { + return Buffer.isBuffer(input) ? input : Buffer.from(input); +} + +function cleanAndOpen(path) { + if (existsSync(path)) { + unlinkSync(path); + } + return createWriteStream(path, { flags: "w+" }); +} + +class ProgmemGenerator { + + constructor(options = {}) { + const { outputPath, bytesPerLine = 20, indent = " ", includes = ARDUINO_INCLUDES } = options; + this.options = { outputPath, bytesPerLine, indent, includes }; + } + + apply(compiler) { + compiler.hooks.emit.tapAsync( + { name: 'ProgmemGenerator' }, + (compilation, callback) => { + const { outputPath, bytesPerLine, indent, includes } = this.options; + const fileInfo = []; + const writeStream = cleanAndOpen(resolve(compilation.options.context, outputPath)); + try { + const writeIncludes = () => { + writeStream.write(includes); + } + + const writeFile = (relativeFilePath, buffer) => { + const variable = "ESP_REACT_DATA_" + fileInfo.length; + const mimeType = mime.lookup(relativeFilePath); + var size = 0; + writeStream.write("const uint8_t " + variable + "[] PROGMEM = {"); + const zipBuffer = zlib.gzipSync(buffer); + zipBuffer.forEach((b) => { + if (!(size % bytesPerLine)) { + writeStream.write("\n"); + writeStream.write(indent); + } + writeStream.write("0x" + ("00" + b.toString(16).toUpperCase()).substr(-2) + ","); + size++; + }); + if (size % bytesPerLine) { + writeStream.write("\n"); + } + writeStream.write("};\n\n"); + fileInfo.push({ + uri: '/' + relativeFilePath.replace(sep, '/'), + mimeType, + variable, + size + }); + }; + + const writeFiles = () => { + // process static files + const buildPath = compilation.options.output.path; + for (const filePath of getFilesSync(buildPath)) { + const readStream = readFileSync(filePath); + const relativeFilePath = relative(buildPath, filePath); + writeFile(relativeFilePath, readStream); + } + // process assets + const { assets } = compilation; + Object.keys(assets).forEach((relativeFilePath) => { + writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source())); + }); + } + + const generateWWWClass = () => { + return `typedef std::function RouteRegistrationHandler; + +class WWWData { +${indent}public: +${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) { +${fileInfo.map(file => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`).join('\n')} +${indent.repeat(2)}} +}; +`; + } + + const writeWWWClass = () => { + writeStream.write(generateWWWClass()); + } + + writeIncludes(); + writeFiles(); + writeWWWClass(); + + writeStream.on('finish', () => { + callback(); + }); + } finally { + writeStream.end(); + } + } + ); + } +} + +module.exports = ProgmemGenerator; diff --git a/interface/public/app/icon.png b/interface/public/app/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..13dd442c80a693e407fb8c2615f699e4639d3eea GIT binary patch literal 8940 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 literal 0 HcmV?d00001 diff --git a/interface/public/app/manifest.json b/interface/public/app/manifest.json new file mode 100644 index 0000000..f775661 --- /dev/null +++ b/interface/public/app/manifest.json @@ -0,0 +1,12 @@ +{ + "name":"ESP8266 React", + "icons":[ + { + "src":"/app/icon.png", + "sizes":"48x48 72x72 96x96 128x128 256x256" + } + ], + "start_url":"/", + "display":"fullscreen", + "orientation":"any" +} diff --git a/interface/public/css/roboto.css b/interface/public/css/roboto.css new file mode 100644 index 0000000..ac21f0f --- /dev/null +++ b/interface/public/css/roboto.css @@ -0,0 +1,22 @@ +/* Just supporting latin due to size constrains on the esp chip */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/li.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215; +} +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'), url(../fonts/re.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215; +} +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/me.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215; +} \ No newline at end of file diff --git a/interface/public/favicon.ico b/interface/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..399ccae7c23cffc07389cf431b329ac611fc6c01 GIT binary patch literal 1150 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 O6y7R}gQBj@I4BA`2c0zl literal 0 HcmV?d00001 diff --git a/interface/public/index.html b/interface/public/index.html new file mode 100644 index 0000000..209254a --- /dev/null +++ b/interface/public/index.html @@ -0,0 +1,16 @@ + + + + + + + + ESP8266 React + + + +
+ + diff --git a/interface/src/App.tsx b/interface/src/App.tsx new file mode 100644 index 0000000..532050e --- /dev/null +++ b/interface/src/App.tsx @@ -0,0 +1,50 @@ +import React, { Component, RefObject } from 'react'; +import { Redirect, Route, Switch } from 'react-router'; +import { SnackbarProvider } from 'notistack'; + +import { IconButton } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; + +import AppRouting from './AppRouting'; +import CustomMuiTheme from './CustomMuiTheme'; +import { PROJECT_NAME } from './api'; +import FeaturesWrapper from './features/FeaturesWrapper'; + +// this redirect forces a call to authenticationContext.refresh() which invalidates the JWT if it is invalid. +const unauthorizedRedirect = () => ; + +class App extends Component { + + notistackRef: RefObject = React.createRef(); + + componentDidMount() { + document.title = PROJECT_NAME; + } + + onClickDismiss = (key: string | number | undefined) => () => { + this.notistackRef.current.closeSnackbar(key); + } + + render() { + return ( + + ( + + + + )}> + + + + + + + + + ); + } +} + +export default App diff --git a/interface/src/AppRouting.tsx b/interface/src/AppRouting.tsx new file mode 100644 index 0000000..f17d5b5 --- /dev/null +++ b/interface/src/AppRouting.tsx @@ -0,0 +1,60 @@ +import React, { Component } from 'react'; +import { Switch, Redirect } from 'react-router'; + +import * as Authentication from './authentication/Authentication'; +import AuthenticationWrapper from './authentication/AuthenticationWrapper'; +import UnauthenticatedRoute from './authentication/UnauthenticatedRoute'; +import AuthenticatedRoute from './authentication/AuthenticatedRoute'; + +import SignIn from './SignIn'; +import ProjectRouting from './project/ProjectRouting'; +import WiFiConnection from './wifi/WiFiConnection'; +import AccessPoint from './ap/AccessPoint'; +import NetworkTime from './ntp/NetworkTime'; +import Security from './security/Security'; +import System from './system/System'; + +import { PROJECT_PATH } from './api'; +import Mqtt from './mqtt/Mqtt'; +import { withFeatures, WithFeaturesProps } from './features/FeaturesContext'; +import { Features } from './features/types'; + +export const getDefaultRoute = (features: Features) => features.project ? `/${PROJECT_PATH}/` : "/wifi/"; + +class AppRouting extends Component { + + componentDidMount() { + Authentication.clearLoginRedirect(); + } + + render() { + const { features } = this.props; + return ( + + + {features.security && ( + + )} + {features.project && ( + + )} + + + {features.ntp && ( + + )} + {features.mqtt && ( + + )} + {features.security && ( + + )} + + + + + ) + } +} + +export default withFeatures(AppRouting); diff --git a/interface/src/CustomMuiTheme.tsx b/interface/src/CustomMuiTheme.tsx new file mode 100644 index 0000000..570ed7f --- /dev/null +++ b/interface/src/CustomMuiTheme.tsx @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; + +import { CssBaseline } from '@material-ui/core'; +import { MuiThemeProvider, createMuiTheme, StylesProvider } from '@material-ui/core/styles'; +import { blueGrey, indigo, orange, red, green } from '@material-ui/core/colors'; + +const theme = createMuiTheme({ + palette: { + primary: indigo, + secondary: blueGrey, + info: { + main: blueGrey[900] + }, + warning: { + main: orange[500] + }, + error: { + main: red[500] + }, + success: { + main: green[500] + } + }, +}); + +export default class CustomMuiTheme extends Component { + + render() { + return ( + + + + {this.props.children} + + + ); + } + +} diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx new file mode 100644 index 0000000..e4df446 --- /dev/null +++ b/interface/src/SignIn.tsx @@ -0,0 +1,147 @@ +import React, { Component } from 'react'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; + +import { withStyles, createStyles, Theme, WithStyles } from '@material-ui/core/styles'; +import { Paper, Typography, Fab } from '@material-ui/core'; +import ForwardIcon from '@material-ui/icons/Forward'; + +import { withAuthenticationContext, AuthenticationContextProps } from './authentication/AuthenticationContext'; +import {PasswordValidator} from './components'; +import { PROJECT_NAME, SIGN_IN_ENDPOINT } from './api'; + +const styles = (theme: Theme) => createStyles({ + signInPage: { + display: "flex", + height: "100vh", + margin: "auto", + padding: theme.spacing(2), + justifyContent: "center", + flexDirection: "column", + maxWidth: theme.breakpoints.values.sm + }, + signInPanel: { + textAlign: "center", + padding: theme.spacing(2), + paddingTop: "200px", + backgroundImage: 'url("/app/icon.png")', + backgroundRepeat: "no-repeat", + backgroundPosition: "50% " + theme.spacing(2) + "px", + backgroundSize: "auto 150px", + width: "100%" + }, + extendedIcon: { + marginRight: theme.spacing(0.5), + }, + button: { + marginRight: theme.spacing(2), + marginTop: theme.spacing(2), + } +}); + +type SignInProps = WithSnackbarProps & WithStyles & AuthenticationContextProps; + +interface SignInState { + username: string, + password: string, + processing: boolean +} + +class SignIn extends Component { + + constructor(props: SignInProps) { + super(props); + this.state = { + username: '', + password: '', + processing: false + }; + } + + updateInputElement = (event: React.ChangeEvent): void => { + const { name, value } = event.currentTarget; + this.setState(prevState => ({ + ...prevState, + [name]: value, + })) + }; + + onSubmit = () => { + const { username, password } = this.state; + const { authenticationContext } = this.props; + this.setState({ processing: true }); + fetch(SIGN_IN_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ username, password }), + headers: new Headers({ + 'Content-Type': 'application/json' + }) + }) + .then(response => { + if (response.status === 200) { + return response.json(); + } else if (response.status === 401) { + throw Error("Invalid credentials."); + } else { + throw Error("Invalid status code: " + response.status); + } + }).then(json => { + authenticationContext.signIn(json.access_token); + }) + .catch(error => { + this.props.enqueueSnackbar(error.message, { + variant: 'warning', + }); + this.setState({ processing: false }); + }); + }; + + render() { + const { username, password, processing } = this.state; + const { classes } = this.props; + return ( +
+ + {PROJECT_NAME} + + + + + + Sign In + + + +
+ ); + } + +} + +export default withAuthenticationContext(withSnackbar(withStyles(styles)(SignIn))); diff --git a/interface/src/ap/APModes.ts b/interface/src/ap/APModes.ts new file mode 100644 index 0000000..6a705db --- /dev/null +++ b/interface/src/ap/APModes.ts @@ -0,0 +1,5 @@ +import { APSettings, APProvisionMode } from "./types"; + +export const isAPEnabled = ({ provision_mode }: APSettings) => { + return provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED; +} diff --git a/interface/src/ap/APSettingsController.tsx b/interface/src/ap/APSettingsController.tsx new file mode 100644 index 0000000..3284b4a --- /dev/null +++ b/interface/src/ap/APSettingsController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import { AP_SETTINGS_ENDPOINT } from '../api'; +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; + +import APSettingsForm from './APSettingsForm'; +import { APSettings } from './types'; + +type APSettingsControllerProps = RestControllerProps; + +class APSettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } + +} + +export default restController(AP_SETTINGS_ENDPOINT, APSettingsController); diff --git a/interface/src/ap/APSettingsForm.tsx b/interface/src/ap/APSettingsForm.tsx new file mode 100644 index 0000000..7f2d21c --- /dev/null +++ b/interface/src/ap/APSettingsForm.tsx @@ -0,0 +1,147 @@ +import React, { Fragment } from 'react'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; +import { range } from 'lodash'; + +import MenuItem from '@material-ui/core/MenuItem'; +import Checkbox from '@material-ui/core/Checkbox'; +import SaveIcon from '@material-ui/icons/Save'; + +import { PasswordValidator, RestFormProps, FormActions, FormButton, BlockFormControlLabel } from '../components'; + +import { isAPEnabled } from './APModes'; +import { APSettings, APProvisionMode } from './types'; +import { isIP } from '../validators'; +import { TextField } from '@material-ui/core'; + +type APSettingsFormProps = RestFormProps; + +class APSettingsForm extends React.Component { + + componentWillMount() { + ValidatorForm.addValidationRule('isIP', isIP); + } + + render() { + const { data, handleValueChange, saveData } = this.props; + return ( + + + Always + When WiFi Disconnected + Never + + { + isAPEnabled(data) && + + + + + { + range(1, 14).map(i => {i}) + } + + + } + label="Hide SSID?" + /> + + { + range(1, 9).map(i => {i}) + } + + + + + + } + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } +} + +export default APSettingsForm; diff --git a/interface/src/ap/APStatus.ts b/interface/src/ap/APStatus.ts new file mode 100644 index 0000000..c35cadc --- /dev/null +++ b/interface/src/ap/APStatus.ts @@ -0,0 +1,28 @@ +import { Theme } from "@material-ui/core"; +import { APStatus, APNetworkStatus } from "./types"; + +export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => { + switch (status) { + case APNetworkStatus.ACTIVE: + return theme.palette.success.main; + case APNetworkStatus.INACTIVE: + return theme.palette.info.main; + case APNetworkStatus.LINGERING: + return theme.palette.warning.main; + default: + return theme.palette.warning.main; + } +} + +export const apStatus = ({ status }: APStatus) => { + switch (status) { + case APNetworkStatus.ACTIVE: + return "Active"; + case APNetworkStatus.INACTIVE: + return "Inactive"; + case APNetworkStatus.LINGERING: + return "Lingering until idle"; + default: + return "Unknown"; + } +}; diff --git a/interface/src/ap/APStatusController.tsx b/interface/src/ap/APStatusController.tsx new file mode 100644 index 0000000..e406bca --- /dev/null +++ b/interface/src/ap/APStatusController.tsx @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { AP_STATUS_ENDPOINT } from '../api'; + +import APStatusForm from './APStatusForm'; +import { APStatus } from './types'; + +type APStatusControllerProps = RestControllerProps; + +class APStatusController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } +} + +export default restController(AP_STATUS_ENDPOINT, APStatusController); diff --git a/interface/src/ap/APStatusForm.tsx b/interface/src/ap/APStatusForm.tsx new file mode 100644 index 0000000..88c2135 --- /dev/null +++ b/interface/src/ap/APStatusForm.tsx @@ -0,0 +1,78 @@ +import React, { Component, Fragment } from 'react'; + +import { WithTheme, withTheme } from '@material-ui/core/styles'; +import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; + +import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; +import DeviceHubIcon from '@material-ui/icons/DeviceHub'; +import ComputerIcon from '@material-ui/icons/Computer'; +import RefreshIcon from '@material-ui/icons/Refresh'; + +import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components'; +import { apStatusHighlight, apStatus } from './APStatus'; +import { APStatus } from './types'; + +type APStatusFormProps = RestFormProps & WithTheme; + +class APStatusForm extends Component { + + createListItems() { + const { data, theme } = this.props + return ( + + + + + + + + + + + + + IP + + + + + + + + + + + + + + + + + + + + + + + + ); + } + + render() { + return ( + + + {this.createListItems()} + + + } variant="contained" color="secondary" onClick={this.props.loadData}> + Refresh + + + + ); + } + +} + +export default withTheme(APStatusForm); diff --git a/interface/src/ap/AccessPoint.tsx b/interface/src/ap/AccessPoint.tsx new file mode 100644 index 0000000..eba011e --- /dev/null +++ b/interface/src/ap/AccessPoint.tsx @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { AuthenticatedContextProps, withAuthenticatedContext, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; + +import APSettingsController from './APSettingsController'; +import APStatusController from './APStatusController'; + +type AccessPointProps = AuthenticatedContextProps & RouteComponentProps; + +class AccessPoint extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + const { authenticatedContext } = this.props; + return ( + + + + + + + + + + + + ) + } +} + +export default withAuthenticatedContext(AccessPoint); diff --git a/interface/src/ap/types.ts b/interface/src/ap/types.ts new file mode 100644 index 0000000..f2d81cd --- /dev/null +++ b/interface/src/ap/types.ts @@ -0,0 +1,30 @@ +export enum APProvisionMode { + AP_MODE_ALWAYS = 0, + AP_MODE_DISCONNECTED = 1, + AP_NEVER = 2 +} + +export enum APNetworkStatus { + ACTIVE = 0, + INACTIVE = 1, + LINGERING = 2 +} + +export interface APStatus { + status: APNetworkStatus; + ip_address: string; + mac_address: string; + station_num: number; +} + +export interface APSettings { + provision_mode: APProvisionMode; + ssid: string; + password: string; + channel: number; + ssid_hidden: boolean; + max_clients: number; + local_ip: string; + gateway_ip: string; + subnet_mask: string; +} diff --git a/interface/src/api/Endpoints.ts b/interface/src/api/Endpoints.ts new file mode 100644 index 0000000..a0be3d4 --- /dev/null +++ b/interface/src/api/Endpoints.ts @@ -0,0 +1,22 @@ +import { ENDPOINT_ROOT } from './Env'; + +export const FEATURES_ENDPOINT = ENDPOINT_ROOT + "features"; +export const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + "ntpStatus"; +export const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "ntpSettings"; +export const TIME_ENDPOINT = ENDPOINT_ROOT + "time"; +export const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "apSettings"; +export const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + "apStatus"; +export const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "scanNetworks"; +export const LIST_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "listNetworks"; +export const WIFI_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "wifiSettings"; +export const WIFI_STATUS_ENDPOINT = ENDPOINT_ROOT + "wifiStatus"; +export const OTA_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "otaSettings"; +export const UPLOAD_FIRMWARE_ENDPOINT = ENDPOINT_ROOT + "uploadFirmware"; +export const MQTT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "mqttSettings"; +export const MQTT_STATUS_ENDPOINT = ENDPOINT_ROOT + "mqttStatus"; +export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus"; +export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn"; +export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization"; +export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings"; +export const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart"; +export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + "factoryReset"; diff --git a/interface/src/api/Env.ts b/interface/src/api/Env.ts new file mode 100644 index 0000000..9992e68 --- /dev/null +++ b/interface/src/api/Env.ts @@ -0,0 +1,24 @@ +export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME!; +export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!; + +export const ENDPOINT_ROOT = calculateEndpointRoot("/rest/"); +export const WEB_SOCKET_ROOT = calculateWebSocketRoot("/ws/"); + +function calculateEndpointRoot(endpointPath: string) { + const httpRoot = process.env.REACT_APP_HTTP_ROOT; + if (httpRoot) { + return httpRoot + endpointPath; + } + const location = window.location; + return location.protocol + "//" + location.host + endpointPath; +} + +function calculateWebSocketRoot(webSocketPath: string) { + const webSocketRoot = process.env.REACT_APP_WEB_SOCKET_ROOT; + if (webSocketRoot) { + return webSocketRoot + webSocketPath; + } + const location = window.location; + const webProtocol = location.protocol === "https:" ? "wss:" : "ws:"; + return webProtocol + "//" + location.host + webSocketPath; +} diff --git a/interface/src/api/index.ts b/interface/src/api/index.ts new file mode 100644 index 0000000..8ec5797 --- /dev/null +++ b/interface/src/api/index.ts @@ -0,0 +1,2 @@ +export * from './Env' +export * from './Endpoints' diff --git a/interface/src/authentication/AuthenticatedRoute.tsx b/interface/src/authentication/AuthenticatedRoute.tsx new file mode 100644 index 0000000..e07b69d --- /dev/null +++ b/interface/src/authentication/AuthenticatedRoute.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-dom"; +import { withSnackbar, WithSnackbarProps } from 'notistack'; + +import * as Authentication from './Authentication'; +import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext } from './AuthenticationContext'; + +type ChildComponent = React.ComponentType> | React.ComponentType; + +interface AuthenticatedRouteProps extends RouteProps, WithSnackbarProps, AuthenticationContextProps { + component: ChildComponent; +} + +type RenderComponent = (props: RouteComponentProps) => React.ReactNode; + +export class AuthenticatedRoute extends React.Component { + + render() { + const { enqueueSnackbar, authenticationContext, component: Component, ...rest } = this.props; + const { location } = this.props; + const renderComponent: RenderComponent = (props) => { + if (authenticationContext.me) { + return ( + + + + ); + } + Authentication.storeLoginRedirect(location); + enqueueSnackbar("Please sign in to continue.", { variant: 'info' }); + return ( + + ); + } + return ( + + ); + } + +} + +export default withSnackbar(withAuthenticationContext(AuthenticatedRoute)); diff --git a/interface/src/authentication/Authentication.ts b/interface/src/authentication/Authentication.ts new file mode 100644 index 0000000..4c87493 --- /dev/null +++ b/interface/src/authentication/Authentication.ts @@ -0,0 +1,114 @@ +import * as H from 'history'; + +import history from '../history'; +import { Features } from '../features/types'; +import { getDefaultRoute } from '../AppRouting'; + +export const ACCESS_TOKEN = 'access_token'; +export const SIGN_IN_PATHNAME = 'signInPathname'; +export const SIGN_IN_SEARCH = 'signInSearch'; + +/** + * Fallback to sessionStorage if localStorage is absent. WebView may not have local storage enabled. + */ +export function getStorage() { + return localStorage || sessionStorage; +} + +export function storeLoginRedirect(location?: H.Location) { + if (location) { + getStorage().setItem(SIGN_IN_PATHNAME, location.pathname); + getStorage().setItem(SIGN_IN_SEARCH, location.search); + } +} + +export function clearLoginRedirect() { + getStorage().removeItem(SIGN_IN_PATHNAME); + getStorage().removeItem(SIGN_IN_SEARCH); +} + +export function fetchLoginRedirect(features: Features): H.LocationDescriptorObject { + const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME); + const signInSearch = getStorage().getItem(SIGN_IN_SEARCH); + clearLoginRedirect(); + return { + pathname: signInPathname || getDefaultRoute(features), + search: (signInPathname && signInSearch) || undefined + }; +} + +/** + * Wraps the normal fetch routene with one with provides the access token if present. + */ +export function authorizedFetch(url: RequestInfo, params?: RequestInit): Promise { + const accessToken = getStorage().getItem(ACCESS_TOKEN); + if (accessToken) { + params = params || {}; + params.credentials = 'include'; + params.headers = { + ...params.headers, + "Authorization": 'Bearer ' + accessToken + }; + } + return fetch(url, params); +} + +/** + * fetch() does not yet support upload progress, this wrapper allows us to configure the xhr request + * for a single file upload and takes care of adding the Authroization header and redirecting on + * authroization errors as we do for normal fetch operations. + */ +export function redirectingAuthorizedUpload(xhr: XMLHttpRequest, url: string, file: File, onProgress: (event: ProgressEvent) => void): Promise { + return new Promise((resolve, reject) => { + xhr.open("POST", url, true); + const accessToken = getStorage().getItem(ACCESS_TOKEN); + if (accessToken) { + xhr.withCredentials = true; + xhr.setRequestHeader("Authorization", 'Bearer ' + accessToken); + } + xhr.upload.onprogress = onProgress; + xhr.onload = function () { + if (xhr.status === 401 || xhr.status === 403) { + history.push("/unauthorized"); + } else { + resolve(); + } + }; + xhr.onerror = function (event: ProgressEvent) { + reject(new DOMException('Error', 'UploadError')); + }; + xhr.onabort = function () { + reject(new DOMException('Aborted', 'AbortError')); + }; + const formData = new FormData(); + formData.append('file', file); + xhr.send(formData); + }); +} + +/** + * Wraps the normal fetch routene which redirects on 401 response. + */ +export function redirectingAuthorizedFetch(url: RequestInfo, params?: RequestInit): Promise { + return new Promise((resolve, reject) => { + authorizedFetch(url, params).then(response => { + if (response.status === 401 || response.status === 403) { + history.push("/unauthorized"); + } else { + resolve(response); + } + }).catch(error => { + reject(error); + }); + }); +} + +export function addAccessTokenParameter(url: string) { + const accessToken = getStorage().getItem(ACCESS_TOKEN); + if (!accessToken) { + return url; + } + const parsedUrl = new URL(url); + parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken); + return parsedUrl.toString(); +} diff --git a/interface/src/authentication/AuthenticationContext.tsx b/interface/src/authentication/AuthenticationContext.tsx new file mode 100644 index 0000000..aaf22ba --- /dev/null +++ b/interface/src/authentication/AuthenticationContext.tsx @@ -0,0 +1,59 @@ +import * as React from "react"; + +export interface Me { + username: string; + admin: boolean; +} + +export interface AuthenticationContext { + refresh: () => void; + signIn: (accessToken: string) => void; + signOut: () => void; + me?: Me; +} + +const AuthenticationContextDefaultValue = {} as AuthenticationContext +export const AuthenticationContext = React.createContext( + AuthenticationContextDefaultValue +); + +export interface AuthenticationContextProps { + authenticationContext: AuthenticationContext; +} + +export function withAuthenticationContext(Component: React.ComponentType) { + return class extends React.Component> { + render() { + return ( + + {authenticationContext => } + + ); + } + }; +} + +export interface AuthenticatedContext extends AuthenticationContext { + me: Me; +} + +const AuthenticatedContextDefaultValue = {} as AuthenticatedContext +export const AuthenticatedContext = React.createContext( + AuthenticatedContextDefaultValue +); + +export interface AuthenticatedContextProps { + authenticatedContext: AuthenticatedContext; +} + +export function withAuthenticatedContext(Component: React.ComponentType) { + return class extends React.Component> { + render() { + return ( + + {authenticatedContext => } + + ); + } + }; +} diff --git a/interface/src/authentication/AuthenticationWrapper.tsx b/interface/src/authentication/AuthenticationWrapper.tsx new file mode 100644 index 0000000..2b65a13 --- /dev/null +++ b/interface/src/authentication/AuthenticationWrapper.tsx @@ -0,0 +1,109 @@ +import * as React from 'react'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; +import jwtDecode from 'jwt-decode'; + +import history from '../history' +import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api'; +import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication'; +import { AuthenticationContext, Me } from './AuthenticationContext'; +import FullScreenLoading from '../components/FullScreenLoading'; +import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext'; + +export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me; + +interface AuthenticationWrapperState { + context: AuthenticationContext; + initialized: boolean; +} + +type AuthenticationWrapperProps = WithSnackbarProps & WithFeaturesProps; + +class AuthenticationWrapper extends React.Component { + + constructor(props: AuthenticationWrapperProps) { + super(props); + this.state = { + context: { + refresh: this.refresh, + signIn: this.signIn, + signOut: this.signOut, + }, + initialized: false + }; + } + + componentDidMount() { + this.refresh(); + } + + render() { + return ( + + {this.state.initialized ? this.renderContent() : this.renderContentLoading()} + + ); + } + + renderContent() { + return ( + + {this.props.children} + + ); + } + + renderContentLoading() { + return ( + + ); + } + + refresh = () => { + if (!this.props.features.security) { + this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } }); + return; + } + const accessToken = getStorage().getItem(ACCESS_TOKEN) + if (accessToken) { + authorizedFetch(VERIFY_AUTHORIZATION_ENDPOINT) + .then(response => { + const me = response.status === 200 ? decodeMeJWT(accessToken) : undefined; + this.setState({ initialized: true, context: { ...this.state.context, me } }); + }).catch(error => { + this.setState({ initialized: true, context: { ...this.state.context, me: undefined } }); + this.props.enqueueSnackbar("Error verifying authorization: " + error.message, { + variant: 'error', + }); + }); + } else { + this.setState({ initialized: true, context: { ...this.state.context, me: undefined } }); + } + } + + signIn = (accessToken: string) => { + try { + getStorage().setItem(ACCESS_TOKEN, accessToken); + const me: Me = decodeMeJWT(accessToken); + this.setState({ context: { ...this.state.context, me } }); + this.props.enqueueSnackbar(`Logged in as ${me.username}`, { variant: 'success' }); + } catch (err) { + this.setState({ initialized: true, context: { ...this.state.context, me: undefined } }); + throw new Error("Failed to parse JWT " + err.message); + } + } + + signOut = () => { + getStorage().removeItem(ACCESS_TOKEN); + this.setState({ + context: { + ...this.state.context, + me: undefined + } + }); + this.props.enqueueSnackbar("You have signed out.", { variant: 'success', }); + history.push('/'); + } + +} + +export default withFeatures(withSnackbar(AuthenticationWrapper)) diff --git a/interface/src/authentication/UnauthenticatedRoute.tsx b/interface/src/authentication/UnauthenticatedRoute.tsx new file mode 100644 index 0000000..25689fd --- /dev/null +++ b/interface/src/authentication/UnauthenticatedRoute.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-dom"; + +import { withAuthenticationContext, AuthenticationContextProps } from './AuthenticationContext'; +import * as Authentication from './Authentication'; +import { WithFeaturesProps, withFeatures } from '../features/FeaturesContext'; + +interface UnauthenticatedRouteProps extends RouteProps, AuthenticationContextProps, WithFeaturesProps { + component: React.ComponentType> | React.ComponentType; +} + +type RenderComponent = (props: RouteComponentProps) => React.ReactNode; + +class UnauthenticatedRoute extends Route { + + public render() { + const { authenticationContext, component: Component, features, ...rest } = this.props; + const renderComponent: RenderComponent = (props) => { + if (authenticationContext.me) { + return (); + } + return (); + } + return ( + + ); + } +} + +export default withFeatures(withAuthenticationContext(UnauthenticatedRoute)); diff --git a/interface/src/authentication/index.ts b/interface/src/authentication/index.ts new file mode 100644 index 0000000..fa528ff --- /dev/null +++ b/interface/src/authentication/index.ts @@ -0,0 +1,6 @@ +export { default as AuthenticatedRoute } from './AuthenticatedRoute'; +export { default as AuthenticationWrapper } from './AuthenticationWrapper'; +export { default as UnauthenticatedRoute } from './UnauthenticatedRoute'; + +export * from './Authentication'; +export * from './AuthenticationContext'; \ No newline at end of file diff --git a/interface/src/components/ApplicationError.tsx b/interface/src/components/ApplicationError.tsx new file mode 100644 index 0000000..7fbe0fc --- /dev/null +++ b/interface/src/components/ApplicationError.tsx @@ -0,0 +1,59 @@ +import React, { FC } from 'react'; +import { makeStyles } from '@material-ui/styles'; +import { Paper, Typography, Box, CssBaseline } from "@material-ui/core"; +import WarningIcon from "@material-ui/icons/Warning" + +const styles = makeStyles( + { + siteErrorPage: { + display: "flex", + height: "100vh", + justifyContent: "center", + flexDirection: "column" + }, + siteErrorPagePanel: { + textAlign: "center", + padding: "280px 0 40px 0", + backgroundImage: 'url("/app/icon.png")', + backgroundRepeat: "no-repeat", + backgroundPosition: "50% 40px", + backgroundSize: "200px auto", + width: "100%", + } + } +); + +interface ApplicationErrorProps { + error?: string; +} + +const ApplicationError: FC = ({ error }) => { + const classes = styles(); + return ( +
+ + + + + + + Application error + + + + + Failed to configure the application, please refresh to try again. + + {error && + ( + + Error: {error} + + ) + } + +
+ ); +} + +export default ApplicationError; diff --git a/interface/src/components/BlockFormControlLabel.tsx b/interface/src/components/BlockFormControlLabel.tsx new file mode 100644 index 0000000..2ea2eba --- /dev/null +++ b/interface/src/components/BlockFormControlLabel.tsx @@ -0,0 +1,10 @@ +import React, { FC } from "react"; +import { FormControlLabel, FormControlLabelProps } from "@material-ui/core"; + +const BlockFormControlLabel: FC = (props) => ( +
+ +
+) + +export default BlockFormControlLabel; diff --git a/interface/src/components/ErrorButton.tsx b/interface/src/components/ErrorButton.tsx new file mode 100644 index 0000000..c93cddd --- /dev/null +++ b/interface/src/components/ErrorButton.tsx @@ -0,0 +1,11 @@ +import { Button, styled } from "@material-ui/core"; + +const ErrorButton = styled(Button)(({ theme }) => ({ + color: theme.palette.getContrastText(theme.palette.error.main), + backgroundColor: theme.palette.error.main, + '&:hover': { + backgroundColor: theme.palette.error.dark, + } +})); + +export default ErrorButton; diff --git a/interface/src/components/FormActions.tsx b/interface/src/components/FormActions.tsx new file mode 100644 index 0000000..8c6eb73 --- /dev/null +++ b/interface/src/components/FormActions.tsx @@ -0,0 +1,7 @@ +import { styled, Box } from "@material-ui/core"; + +const FormActions = styled(Box)(({ theme }) => ({ + marginTop: theme.spacing(1) +})); + +export default FormActions; diff --git a/interface/src/components/FormButton.tsx b/interface/src/components/FormButton.tsx new file mode 100644 index 0000000..f14ef06 --- /dev/null +++ b/interface/src/components/FormButton.tsx @@ -0,0 +1,13 @@ +import { Button, styled } from "@material-ui/core"; + +const FormButton = styled(Button)(({ theme }) => ({ + margin: theme.spacing(0, 1), + '&:last-child': { + marginRight: 0, + }, + '&:first-child': { + marginLeft: 0, + } +})); + +export default FormButton; diff --git a/interface/src/components/FullScreenLoading.tsx b/interface/src/components/FullScreenLoading.tsx new file mode 100644 index 0000000..d08d904 --- /dev/null +++ b/interface/src/components/FullScreenLoading.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import { Typography, Theme } from '@material-ui/core'; +import { makeStyles, createStyles } from '@material-ui/styles'; + +const useStyles = makeStyles((theme: Theme) => createStyles({ + fullScreenLoading: { + padding: theme.spacing(2), + display: "flex", + alignItems: "center", + justifyContent: "center", + height: "100vh", + flexDirection: "column" + }, + progress: { + margin: theme.spacing(4), + } +})); + +const FullScreenLoading = () => { + const classes = useStyles(); + return ( +
+ + + Loading… + +
+ ) +} + +export default FullScreenLoading; diff --git a/interface/src/components/HighlightAvatar.tsx b/interface/src/components/HighlightAvatar.tsx new file mode 100644 index 0000000..f7ce8c7 --- /dev/null +++ b/interface/src/components/HighlightAvatar.tsx @@ -0,0 +1,23 @@ +import { Avatar, makeStyles } from "@material-ui/core"; +import React, { FC } from "react"; + +interface HighlightAvatarProps { + color: string; +} + +const useStyles = makeStyles({ + root: (props: HighlightAvatarProps) => ({ + backgroundColor: props.color + }) +}); + +const HighlightAvatar: FC = (props) => { + const classes = useStyles(props); + return ( + + {props.children} + + ); +} + +export default HighlightAvatar; diff --git a/interface/src/components/MenuAppBar.tsx b/interface/src/components/MenuAppBar.tsx new file mode 100644 index 0000000..dfe5f22 --- /dev/null +++ b/interface/src/components/MenuAppBar.tsx @@ -0,0 +1,286 @@ +import React, { RefObject, Fragment } from 'react'; +import { Link, withRouter, RouteComponentProps } from 'react-router-dom'; + +import { Drawer, AppBar, Toolbar, Avatar, Divider, Button, Box, IconButton } from '@material-ui/core'; +import { ClickAwayListener, Popper, Hidden, Typography } from '@material-ui/core'; +import { List, ListItem, ListItemIcon, ListItemText, ListItemAvatar } from '@material-ui/core'; +import { Card, CardContent, CardActions } from '@material-ui/core'; + +import { withStyles, createStyles, Theme, WithTheme, WithStyles, withTheme } from '@material-ui/core/styles'; + +import WifiIcon from '@material-ui/icons/Wifi'; +import SettingsIcon from '@material-ui/icons/Settings'; +import AccessTimeIcon from '@material-ui/icons/AccessTime'; +import AccountCircleIcon from '@material-ui/icons/AccountCircle'; +import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; +import DeviceHubIcon from '@material-ui/icons/DeviceHub'; +import LockIcon from '@material-ui/icons/Lock'; +import MenuIcon from '@material-ui/icons/Menu'; + +import ProjectMenu from '../project/ProjectMenu'; +import { PROJECT_NAME } from '../api'; +import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; +import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext'; + +const drawerWidth = 290; + +const styles = (theme: Theme) => createStyles({ + root: { + display: 'flex', + }, + drawer: { + [theme.breakpoints.up('md')]: { + width: drawerWidth, + flexShrink: 0, + }, + }, + title: { + flexGrow: 1 + }, + appBar: { + marginLeft: drawerWidth, + [theme.breakpoints.up('md')]: { + width: `calc(100% - ${drawerWidth}px)`, + }, + }, + toolbarImage: { + [theme.breakpoints.up('xs')]: { + height: 24, + marginRight: theme.spacing(2) + }, + [theme.breakpoints.up('sm')]: { + height: 36, + marginRight: theme.spacing(3) + }, + }, + menuButton: { + marginRight: theme.spacing(2), + [theme.breakpoints.up('md')]: { + display: 'none', + }, + }, + toolbar: theme.mixins.toolbar, + drawerPaper: { + width: drawerWidth, + }, + content: { + flexGrow: 1 + }, + authMenu: { + zIndex: theme.zIndex.tooltip, + maxWidth: 400, + }, + authMenuActions: { + padding: theme.spacing(2), + "& > * + *": { + marginLeft: theme.spacing(2), + } + }, +}); + +interface MenuAppBarState { + mobileOpen: boolean; + authMenuOpen: boolean; +} + +interface MenuAppBarProps extends WithFeaturesProps, AuthenticatedContextProps, WithTheme, WithStyles, RouteComponentProps { + sectionTitle: string; +} + +class MenuAppBar extends React.Component { + + constructor(props: MenuAppBarProps) { + super(props); + this.state = { + mobileOpen: false, + authMenuOpen: false + }; + } + + anchorRef: RefObject = React.createRef(); + + handleToggle = () => { + this.setState({ authMenuOpen: !this.state.authMenuOpen }); + } + + handleClose = (event: React.MouseEvent) => { + if (this.anchorRef.current && this.anchorRef.current.contains(event.currentTarget)) { + return; + } + this.setState({ authMenuOpen: false }); + } + + handleDrawerToggle = () => { + this.setState({ mobileOpen: !this.state.mobileOpen }); + }; + + render() { + const { classes, theme, children, sectionTitle, authenticatedContext, features } = this.props; + const { mobileOpen, authMenuOpen } = this.state; + const path = this.props.match.url; + const drawer = ( +
+ + + {PROJECT_NAME} + + + {PROJECT_NAME} + + + + {features.project && ( + + + + + )} + + + + + + + + + + + + + + {features.ntp && ( + + + + + + + )} + {features.mqtt && ( + + + + + + + )} + {features.security && ( + + + + + + + )} + + + + + + + +
+ ); + + const userMenu = ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); + + return ( +
+ + + + + + + {sectionTitle} + + {features.security && userMenu} + + + +
+
+ {children} +
+
+ ); + } +} + +export default withRouter( + withTheme( + withFeatures( + withAuthenticatedContext( + withStyles(styles)(MenuAppBar) + ) + ) + ) +); diff --git a/interface/src/components/PasswordValidator.tsx b/interface/src/components/PasswordValidator.tsx new file mode 100644 index 0000000..8ee0276 --- /dev/null +++ b/interface/src/components/PasswordValidator.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { TextValidator, ValidatorComponentProps } from 'react-material-ui-form-validator'; + +import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; +import { InputAdornment, IconButton } from '@material-ui/core'; +import {Visibility,VisibilityOff } from '@material-ui/icons'; + +const styles = createStyles({ + input: { + "&::-ms-reveal": { + display: "none" + } + } +}); + +type PasswordValidatorProps = WithStyles & Exclude; + +interface PasswordValidatorState { + showPassword: boolean; +} + +class PasswordValidator extends React.Component { + + state = { + showPassword: false + }; + + toggleShowPassword = () => { + this.setState({ + showPassword: !this.state.showPassword + }); + } + + render() { + const { classes, ...rest } = this.props; + return ( + + + {this.state.showPassword ? : } + + + }} + /> + ); + } + +} + +export default withStyles(styles)(PasswordValidator); diff --git a/interface/src/components/RestController.tsx b/interface/src/components/RestController.tsx new file mode 100644 index 0000000..c9751c6 --- /dev/null +++ b/interface/src/components/RestController.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; + +import { redirectingAuthorizedFetch } from '../authentication'; + +export interface RestControllerProps extends WithSnackbarProps { + handleValueChange: (name: keyof D) => (event: React.ChangeEvent) => void; + + setData: (data: D, callback?: () => void) => void; + saveData: () => void; + loadData: () => void; + + data?: D; + loading: boolean; + errorMessage?: string; +} + +export const extractEventValue = (event: React.ChangeEvent) => { + switch (event.target.type) { + case "number": + return event.target.valueAsNumber; + case "checkbox": + return event.target.checked; + default: + return event.target.value + } +} + +interface RestControllerState { + data?: D; + loading: boolean; + errorMessage?: string; +} + +export function restController>(endpointUrl: string, RestController: React.ComponentType

>) { + return withSnackbar( + class extends React.Component> & WithSnackbarProps, RestControllerState> { + + state: RestControllerState = { + data: undefined, + loading: false, + errorMessage: undefined + }; + + setData = (data: D, callback?: () => void) => { + this.setState({ + data, + loading: false, + errorMessage: undefined + }, callback); + } + + loadData = () => { + this.setState({ + data: undefined, + loading: true, + errorMessage: undefined + }); + redirectingAuthorizedFetch(endpointUrl).then(response => { + if (response.status === 200) { + return response.json(); + } + throw Error("Invalid status code: " + response.status); + }).then(json => { + this.setState({ data: json, loading: false }) + }).catch(error => { + const errorMessage = error.message || "Unknown error"; + this.props.enqueueSnackbar("Problem fetching: " + errorMessage, { variant: 'error' }); + this.setState({ data: undefined, loading: false, errorMessage }); + }); + } + + saveData = () => { + this.setState({ loading: true }); + redirectingAuthorizedFetch(endpointUrl, { + method: 'POST', + body: JSON.stringify(this.state.data), + headers: { + 'Content-Type': 'application/json' + } + }).then(response => { + if (response.status === 200) { + return response.json(); + } + throw Error("Invalid status code: " + response.status); + }).then(json => { + this.props.enqueueSnackbar("Update successful.", { variant: 'success' }); + this.setState({ data: json, loading: false }); + }).catch(error => { + const errorMessage = error.message || "Unknown error"; + this.props.enqueueSnackbar("Problem updating: " + errorMessage, { variant: 'error' }); + this.setState({ data: undefined, loading: false, errorMessage }); + }); + } + + handleValueChange = (name: keyof D) => (event: React.ChangeEvent) => { + const data = { ...this.state.data!, [name]: extractEventValue(event) }; + this.setState({ data }); + } + + render() { + return ; + } + + }); +} diff --git a/interface/src/components/RestFormLoader.tsx b/interface/src/components/RestFormLoader.tsx new file mode 100644 index 0000000..29fb304 --- /dev/null +++ b/interface/src/components/RestFormLoader.tsx @@ -0,0 +1,56 @@ +import React from 'react'; + +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import { Button, LinearProgress, Typography } from '@material-ui/core'; + +import { RestControllerProps } from '.'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + loadingSettings: { + margin: theme.spacing(0.5), + }, + loadingSettingsDetails: { + margin: theme.spacing(4), + textAlign: "center" + }, + button: { + marginRight: theme.spacing(2), + marginTop: theme.spacing(2), + } + }) +); + +export type RestFormProps = Omit, "loading" | "errorMessage"> & { data: D }; + +interface RestFormLoaderProps extends RestControllerProps { + render: (props: RestFormProps) => JSX.Element; +} + +export default function RestFormLoader(props: RestFormLoaderProps) { + const { loading, errorMessage, loadData, render, data, ...rest } = props; + const classes = useStyles(); + if (loading || !data) { + return ( +

+ + + Loading… + +
+ ); + } + if (errorMessage) { + return ( +
+ + {errorMessage} + + +
+ ); + } + return render({ ...rest, loadData, data }); +} diff --git a/interface/src/components/SectionContent.tsx b/interface/src/components/SectionContent.tsx new file mode 100644 index 0000000..457014f --- /dev/null +++ b/interface/src/components/SectionContent.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import { Typography, Paper } from '@material-ui/core'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + content: { + padding: theme.spacing(2), + margin: theme.spacing(3), + } + }) +); + +interface SectionContentProps { + title: string; + titleGutter?: boolean; +} + +const SectionContent: React.FC = (props) => { + const { children, title, titleGutter } = props; + const classes = useStyles(); + return ( + + + {title} + + {children} + + ); +}; + +export default SectionContent; diff --git a/interface/src/components/SingleUpload.tsx b/interface/src/components/SingleUpload.tsx new file mode 100644 index 0000000..003c286 --- /dev/null +++ b/interface/src/components/SingleUpload.tsx @@ -0,0 +1,96 @@ +import React, { FC, Fragment } from 'react'; +import { useDropzone, DropzoneState } from 'react-dropzone'; + +import { makeStyles, createStyles } from '@material-ui/styles'; +import CloudUploadIcon from '@material-ui/icons/CloudUpload'; +import CancelIcon from '@material-ui/icons/Cancel'; +import { Theme, Box, Typography, LinearProgress, Button } from '@material-ui/core'; + +interface SingleUploadStyleProps extends DropzoneState { + uploading: boolean; +} + +const progressPercentage = (progress: ProgressEvent) => Math.round((progress.loaded * 100) / progress.total); + +const getBorderColor = (theme: Theme, props: SingleUploadStyleProps) => { + if (props.isDragAccept) { + return theme.palette.success.main; + } + if (props.isDragReject) { + return theme.palette.error.main; + } + if (props.isDragActive) { + return theme.palette.info.main; + } + return theme.palette.grey[700]; +} + +const useStyles = makeStyles((theme: Theme) => createStyles({ + dropzone: { + padding: theme.spacing(8, 2), + borderWidth: 2, + borderRadius: 2, + borderStyle: 'dashed', + color: theme.palette.grey[700], + transition: 'border .24s ease-in-out', + cursor: (props: SingleUploadStyleProps) => props.uploading ? 'default' : 'pointer', + width: '100%', + borderColor: (props: SingleUploadStyleProps) => getBorderColor(theme, props) + } +})); + +export interface SingleUploadProps { + onDrop: (acceptedFiles: File[]) => void; + onCancel: () => void; + accept?: string | string[]; + uploading: boolean; + progress?: ProgressEvent; +} + +const SingleUpload: FC = ({ onDrop, onCancel, accept, uploading, progress }) => { + const dropzoneState = useDropzone({ onDrop, accept, disabled: uploading, multiple: false }); + const { getRootProps, getInputProps } = dropzoneState; + const classes = useStyles({ ...dropzoneState, uploading }); + + + const renderProgressText = () => { + if (uploading) { + if (progress?.lengthComputable) { + return `Uploading: ${progressPercentage(progress)}%`; + } + return "Uploading\u2026"; + } + return "Drop file or click here"; + } + + const renderProgress = (progress?: ProgressEvent) => ( + + ); + + return ( +
+ + + + + {renderProgressText()} + + {uploading && ( + + + {renderProgress(progress)} + + + + )} + +
+ ); +} + +export default SingleUpload; diff --git a/interface/src/components/WebSocketController.tsx b/interface/src/components/WebSocketController.tsx new file mode 100644 index 0000000..5fe9fa3 --- /dev/null +++ b/interface/src/components/WebSocketController.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import Sockette from 'sockette'; +import throttle from 'lodash/throttle'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; + +import { addAccessTokenParameter } from '../authentication'; +import { extractEventValue } from '.'; + +export interface WebSocketControllerProps extends WithSnackbarProps { + handleValueChange: (name: keyof D) => (event: React.ChangeEvent) => void; + + setData: (data: D, callback?: () => void) => void; + saveData: () => void; + saveDataAndClear(): () => void; + + connected: boolean; + data?: D; +} + +interface WebSocketControllerState { + ws: Sockette; + connected: boolean; + clientId?: string; + data?: D; +} + +enum WebSocketMessageType { + ID = "id", + PAYLOAD = "payload" +} + +interface WebSocketIdMessage { + type: typeof WebSocketMessageType.ID; + id: string; +} + +interface WebSocketPayloadMessage { + type: typeof WebSocketMessageType.PAYLOAD; + origin_id: string; + payload: D; +} + +export type WebSocketMessage = WebSocketIdMessage | WebSocketPayloadMessage; + +export function webSocketController>(wsUrl: string, wsThrottle: number, WebSocketController: React.ComponentType

>) { + return withSnackbar( + class extends React.Component> & WithSnackbarProps, WebSocketControllerState> { + constructor(props: Omit> & WithSnackbarProps) { + super(props); + this.state = { + ws: new Sockette(addAccessTokenParameter(wsUrl), { + onmessage: this.onMessage, + onopen: this.onOpen, + onclose: this.onClose, + }), + connected: false + } + } + + componentWillUnmount() { + this.state.ws.close(); + } + + onMessage = (event: MessageEvent) => { + const rawData = event.data; + if (typeof rawData === 'string' || rawData instanceof String) { + this.handleMessage(JSON.parse(rawData as string) as WebSocketMessage); + } + } + + handleMessage = (message: WebSocketMessage) => { + switch (message.type) { + case WebSocketMessageType.ID: + this.setState({ clientId: message.id }); + break; + case WebSocketMessageType.PAYLOAD: + const { clientId, data } = this.state; + if (clientId && (!data || clientId !== message.origin_id)) { + this.setState( + { data: message.payload } + ); + } + break; + } + } + + onOpen = () => { + this.setState({ connected: true }); + } + + onClose = () => { + this.setState({ connected: false, clientId: undefined, data: undefined }); + } + + setData = (data: D, callback?: () => void) => { + this.setState({ data }, callback); + } + + saveData = throttle(() => { + const { ws, connected, data } = this.state; + if (connected) { + ws.json(data); + } + }, wsThrottle); + + saveDataAndClear = throttle(() => { + const { ws, connected, data } = this.state; + if (connected) { + this.setState({ + data: undefined + }, () => ws.json(data)); + } + }, wsThrottle); + + handleValueChange = (name: keyof D) => (event: React.ChangeEvent) => { + const data = { ...this.state.data!, [name]: extractEventValue(event) }; + this.setState({ data }); + } + + render() { + return ; + } + + }); +} diff --git a/interface/src/components/WebSocketFormLoader.tsx b/interface/src/components/WebSocketFormLoader.tsx new file mode 100644 index 0000000..ee5f335 --- /dev/null +++ b/interface/src/components/WebSocketFormLoader.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import { LinearProgress, Typography } from '@material-ui/core'; + +import { WebSocketControllerProps } from '.'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + loadingSettings: { + margin: theme.spacing(0.5), + }, + loadingSettingsDetails: { + margin: theme.spacing(4), + textAlign: "center" + } + }) +); + +export type WebSocketFormProps = Omit, "connected"> & { data: D }; + +interface WebSocketFormLoaderProps extends WebSocketControllerProps { + render: (props: WebSocketFormProps) => JSX.Element; +} + +export default function WebSocketFormLoader(props: WebSocketFormLoaderProps) { + const { connected, render, data, ...rest } = props; + const classes = useStyles(); + if (!connected || !data) { + return ( +

+ + + Connecting to WebSocket... + +
+ ); + } + return render({ ...rest, data }); +} diff --git a/interface/src/components/index.ts b/interface/src/components/index.ts new file mode 100644 index 0000000..4a6cc6a --- /dev/null +++ b/interface/src/components/index.ts @@ -0,0 +1,17 @@ +export { default as BlockFormControlLabel } from './BlockFormControlLabel'; +export { default as FormActions } from './FormActions'; +export { default as FormButton } from './FormButton'; +export { default as HighlightAvatar } from './HighlightAvatar'; +export { default as MenuAppBar } from './MenuAppBar'; +export { default as PasswordValidator } from './PasswordValidator'; +export { default as RestFormLoader } from './RestFormLoader'; +export { default as SectionContent } from './SectionContent'; +export { default as WebSocketFormLoader } from './WebSocketFormLoader'; +export { default as ErrorButton } from './ErrorButton'; +export { default as SingleUpload } from './SingleUpload'; + +export * from './RestFormLoader'; +export * from './RestController'; + +export * from './WebSocketFormLoader'; +export * from './WebSocketController'; diff --git a/interface/src/features/ApplicationContext.tsx b/interface/src/features/ApplicationContext.tsx new file mode 100644 index 0000000..fbc15f2 --- /dev/null +++ b/interface/src/features/ApplicationContext.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Features } from './types'; + +export interface ApplicationContext { + features: Features; +} + +const ApplicationContextDefaultValue = {} as ApplicationContext +export const ApplicationContext = React.createContext( + ApplicationContextDefaultValue +); + +export function withAuthenticatedContexApplicationContext(Component: React.ComponentType) { + return class extends React.Component> { + render() { + return ( + + {authenticatedContext => } + + ); + } + }; +} diff --git a/interface/src/features/FeaturesContext.tsx b/interface/src/features/FeaturesContext.tsx new file mode 100644 index 0000000..8c8f104 --- /dev/null +++ b/interface/src/features/FeaturesContext.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Features } from './types'; + +export interface FeaturesContext { + features: Features; +} + +const FeaturesContextDefaultValue = {} as FeaturesContext +export const FeaturesContext = React.createContext( + FeaturesContextDefaultValue +); + +export interface WithFeaturesProps { + features: Features; +} + +export function withFeatures(Component: React.ComponentType) { + return class extends React.Component> { + render() { + return ( + + {featuresContext => } + + ); + } + }; +} diff --git a/interface/src/features/FeaturesWrapper.tsx b/interface/src/features/FeaturesWrapper.tsx new file mode 100644 index 0000000..aac3533 --- /dev/null +++ b/interface/src/features/FeaturesWrapper.tsx @@ -0,0 +1,61 @@ +import React, { Component } from 'react'; + +import { Features } from './types'; +import { FeaturesContext } from './FeaturesContext'; +import FullScreenLoading from '../components/FullScreenLoading'; +import ApplicationError from '../components/ApplicationError'; +import { FEATURES_ENDPOINT } from '../api'; + +interface FeaturesWrapperState { + features?: Features; + error?: string; +}; + +class FeaturesWrapper extends Component<{}, FeaturesWrapperState> { + + state: FeaturesWrapperState = {}; + + componentDidMount() { + this.fetchFeaturesDetails(); + } + + fetchFeaturesDetails = () => { + fetch(FEATURES_ENDPOINT) + .then(response => { + if (response.status === 200) { + return response.json(); + } else { + throw Error("Unexpected status code: " + response.status); + } + }).then(features => { + this.setState({ features }); + }) + .catch(error => { + this.setState({ error: error.message }); + }); + } + + render() { + const { features, error } = this.state; + if (features) { + return ( + + {this.props.children} + + ); + } + if (error) { + return ( + + ); + } + return ( + + ); + } + +} + +export default FeaturesWrapper; diff --git a/interface/src/features/types.ts b/interface/src/features/types.ts new file mode 100644 index 0000000..1753d9a --- /dev/null +++ b/interface/src/features/types.ts @@ -0,0 +1,8 @@ +export interface Features { + project: boolean; + security: boolean; + mqtt: boolean; + ntp: boolean; + ota: boolean; + upload_firmware: boolean; +} diff --git a/interface/src/history.ts b/interface/src/history.ts new file mode 100644 index 0000000..eb70d7b --- /dev/null +++ b/interface/src/history.ts @@ -0,0 +1,5 @@ +import { createBrowserHistory } from 'history'; + +export default createBrowserHistory({ + /* pass a configuration object here if needed */ +}) diff --git a/interface/src/index.tsx b/interface/src/index.tsx new file mode 100644 index 0000000..a0801fc --- /dev/null +++ b/interface/src/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { render } from 'react-dom'; + +import history from './history'; +import { Router } from 'react-router'; + +import App from './App'; + +render(( + + + +), document.getElementById("root")) diff --git a/interface/src/mqtt/Mqtt.tsx b/interface/src/mqtt/Mqtt.tsx new file mode 100644 index 0000000..8daca77 --- /dev/null +++ b/interface/src/mqtt/Mqtt.tsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { AuthenticatedContextProps, withAuthenticatedContext, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; +import MqttStatusController from './MqttStatusController'; +import MqttSettingsController from './MqttSettingsController'; + +type MqttProps = AuthenticatedContextProps & RouteComponentProps; + +class Mqtt extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + const { authenticatedContext } = this.props; + return ( + + + + + + + + + + + + ) + } +} + +export default withAuthenticatedContext(Mqtt); diff --git a/interface/src/mqtt/MqttSettingsController.tsx b/interface/src/mqtt/MqttSettingsController.tsx new file mode 100644 index 0000000..8cc9d16 --- /dev/null +++ b/interface/src/mqtt/MqttSettingsController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { MQTT_SETTINGS_ENDPOINT } from '../api'; + +import MqttSettingsForm from './MqttSettingsForm'; +import { MqttSettings } from './types'; + +type MqttSettingsControllerProps = RestControllerProps; + +class MqttSettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } + +} + +export default restController(MQTT_SETTINGS_ENDPOINT, MqttSettingsController); diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx new file mode 100644 index 0000000..fb9c77e --- /dev/null +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; + +import { Checkbox, TextField } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { RestFormProps, FormActions, FormButton, BlockFormControlLabel, PasswordValidator } from '../components'; +import { isIP, isHostname, or } from '../validators'; + +import { MqttSettings } from './types'; + +type MqttSettingsFormProps = RestFormProps; + +class MqttSettingsForm extends React.Component { + + componentDidMount() { + ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname)); + } + + render() { + const { data, handleValueChange, saveData } = this.props; + return ( + + + } + label="Enable MQTT?" + /> + + + + + + + + } + label="Clean Session?" + /> + + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } +} + +export default MqttSettingsForm; diff --git a/interface/src/mqtt/MqttStatus.ts b/interface/src/mqtt/MqttStatus.ts new file mode 100644 index 0000000..b9bb80c --- /dev/null +++ b/interface/src/mqtt/MqttStatus.ts @@ -0,0 +1,45 @@ +import { Theme } from "@material-ui/core"; +import { MqttStatus, MqttDisconnectReason } from "./types"; + +export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: Theme) => { + if (!enabled) { + return theme.palette.info.main; + } + if (connected) { + return theme.palette.success.main; + } + return theme.palette.error.main; +} + +export const mqttStatus = ({ enabled, connected }: MqttStatus) => { + if (!enabled) { + return "Not enabled"; + } + if (connected) { + return "Connected"; + } + return "Disconnected"; +} + +export const disconnectReason = ({ disconnect_reason }: MqttStatus) => { + switch (disconnect_reason) { + case MqttDisconnectReason.TCP_DISCONNECTED: + return "TCP disconnected"; + case MqttDisconnectReason.MQTT_UNACCEPTABLE_PROTOCOL_VERSION: + return "Unacceptable protocol version"; + case MqttDisconnectReason.MQTT_IDENTIFIER_REJECTED: + return "Client ID rejected"; + case MqttDisconnectReason.MQTT_SERVER_UNAVAILABLE: + return "Server unavailable"; + case MqttDisconnectReason.MQTT_MALFORMED_CREDENTIALS: + return "Malformed credentials"; + case MqttDisconnectReason.MQTT_NOT_AUTHORIZED: + return "Not authorized"; + case MqttDisconnectReason.ESP8266_NOT_ENOUGH_SPACE: + return "Device out of memory"; + case MqttDisconnectReason.TLS_BAD_FINGERPRINT: + return "Server fingerprint invalid"; + default: + return "Unknown" + } +} diff --git a/interface/src/mqtt/MqttStatusController.tsx b/interface/src/mqtt/MqttStatusController.tsx new file mode 100644 index 0000000..4dd5409 --- /dev/null +++ b/interface/src/mqtt/MqttStatusController.tsx @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { MQTT_STATUS_ENDPOINT } from '../api'; + +import MqttStatusForm from './MqttStatusForm'; +import { MqttStatus } from './types'; + +type MqttStatusControllerProps = RestControllerProps; + +class MqttStatusController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } +} + +export default restController(MQTT_STATUS_ENDPOINT, MqttStatusController); diff --git a/interface/src/mqtt/MqttStatusForm.tsx b/interface/src/mqtt/MqttStatusForm.tsx new file mode 100644 index 0000000..5a80a41 --- /dev/null +++ b/interface/src/mqtt/MqttStatusForm.tsx @@ -0,0 +1,83 @@ +import React, { Component, Fragment } from 'react'; + +import { WithTheme, withTheme } from '@material-ui/core/styles'; +import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; + +import DeviceHubIcon from '@material-ui/icons/DeviceHub'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import ReportIcon from '@material-ui/icons/Report'; + +import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components'; +import { mqttStatusHighlight, mqttStatus, disconnectReason } from './MqttStatus'; +import { MqttStatus } from './types'; + +type MqttStatusFormProps = RestFormProps & WithTheme; + +class MqttStatusForm extends Component { + + renderConnectionStatus() { + const { data } = this.props + if (data.connected) { + return ( + + + + # + + + + + + ); + } + return ( + + + + + + + + + + + + ); + } + + createListItems() { + const { data, theme } = this.props + return ( + + + + + + + + + + + {data.enabled && this.renderConnectionStatus()} + + ); + } + + render() { + return ( + + + {this.createListItems()} + + + } variant="contained" color="secondary" onClick={this.props.loadData}> + Refresh + + + + ); + } + +} + +export default withTheme(MqttStatusForm); diff --git a/interface/src/mqtt/types.ts b/interface/src/mqtt/types.ts new file mode 100644 index 0000000..04e20ca --- /dev/null +++ b/interface/src/mqtt/types.ts @@ -0,0 +1,29 @@ +export enum MqttDisconnectReason { + TCP_DISCONNECTED = 0, + MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1, + MQTT_IDENTIFIER_REJECTED = 2, + MQTT_SERVER_UNAVAILABLE = 3, + MQTT_MALFORMED_CREDENTIALS = 4, + MQTT_NOT_AUTHORIZED = 5, + ESP8266_NOT_ENOUGH_SPACE = 6, + TLS_BAD_FINGERPRINT = 7 +} + +export interface MqttStatus { + enabled: boolean; + connected: boolean; + client_id: string; + disconnect_reason: MqttDisconnectReason; +} + +export interface MqttSettings { + enabled: boolean; + host: string; + port: number; + username: string; + password: string; + client_id: string; + keep_alive: number; + clean_session: boolean; + max_topic_length: number; +} diff --git a/interface/src/ntp/NTPSettingsController.tsx b/interface/src/ntp/NTPSettingsController.tsx new file mode 100644 index 0000000..78f5f63 --- /dev/null +++ b/interface/src/ntp/NTPSettingsController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { NTP_SETTINGS_ENDPOINT } from '../api'; + +import NTPSettingsForm from './NTPSettingsForm'; +import { NTPSettings } from './types'; + +type NTPSettingsControllerProps = RestControllerProps; + +class NTPSettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } + +} + +export default restController(NTP_SETTINGS_ENDPOINT, NTPSettingsController); diff --git a/interface/src/ntp/NTPSettingsForm.tsx b/interface/src/ntp/NTPSettingsForm.tsx new file mode 100644 index 0000000..c3e4dab --- /dev/null +++ b/interface/src/ntp/NTPSettingsForm.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { TextValidator, ValidatorForm, SelectValidator } from 'react-material-ui-form-validator'; + +import { Checkbox, MenuItem } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { RestFormProps, FormActions, FormButton, BlockFormControlLabel } from '../components'; +import { isIP, isHostname, or } from '../validators'; + +import { TIME_ZONES, timeZoneSelectItems, selectedTimeZone } from './TZ'; +import { NTPSettings } from './types'; + +type NTPSettingsFormProps = RestFormProps; + +class NTPSettingsForm extends React.Component { + + componentDidMount() { + ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname)); + } + + changeTimeZone = (event: React.ChangeEvent) => { + const { data, setData } = this.props; + setData({ + ...data, + tz_label: event.target.value, + tz_format: TIME_ZONES[event.target.value] + }); + } + + render() { + const { data, handleValueChange, saveData } = this.props; + return ( + + + } + label="Enable NTP?" + /> + + + Time zone... + {timeZoneSelectItems()} + + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } +} + +export default NTPSettingsForm; diff --git a/interface/src/ntp/NTPStatus.ts b/interface/src/ntp/NTPStatus.ts new file mode 100644 index 0000000..744b56d --- /dev/null +++ b/interface/src/ntp/NTPStatus.ts @@ -0,0 +1,26 @@ +import { Theme } from "@material-ui/core"; +import { NTPStatus, NTPSyncStatus } from "./types"; + +export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE; + +export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => { + switch (status) { + case NTPSyncStatus.NTP_INACTIVE: + return theme.palette.info.main; + case NTPSyncStatus.NTP_ACTIVE: + return theme.palette.success.main; + default: + return theme.palette.error.main; + } +} + +export const ntpStatus = ({ status }: NTPStatus) => { + switch (status) { + case NTPSyncStatus.NTP_INACTIVE: + return "Inactive"; + case NTPSyncStatus.NTP_ACTIVE: + return "Active"; + default: + return "Unknown"; + } +} diff --git a/interface/src/ntp/NTPStatusController.tsx b/interface/src/ntp/NTPStatusController.tsx new file mode 100644 index 0000000..25ea4de --- /dev/null +++ b/interface/src/ntp/NTPStatusController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { NTP_STATUS_ENDPOINT } from '../api'; + +import NTPStatusForm from './NTPStatusForm'; +import { NTPStatus } from './types'; + +type NTPStatusControllerProps = RestControllerProps; + +class NTPStatusController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(NTP_STATUS_ENDPOINT, NTPStatusController); diff --git a/interface/src/ntp/NTPStatusForm.tsx b/interface/src/ntp/NTPStatusForm.tsx new file mode 100644 index 0000000..6b277dd --- /dev/null +++ b/interface/src/ntp/NTPStatusForm.tsx @@ -0,0 +1,198 @@ +import React, { Component, Fragment } from 'react'; +import moment from 'moment'; + +import { WithTheme, withTheme } from '@material-ui/core/styles'; +import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core'; +import { Dialog, DialogTitle, DialogContent, DialogActions, Box, TextField } from '@material-ui/core'; + +import SwapVerticalCircleIcon from '@material-ui/icons/SwapVerticalCircle'; +import AccessTimeIcon from '@material-ui/icons/AccessTime'; +import DNSIcon from '@material-ui/icons/Dns'; +import UpdateIcon from '@material-ui/icons/Update'; +import AvTimerIcon from '@material-ui/icons/AvTimer'; +import RefreshIcon from '@material-ui/icons/Refresh'; + +import { RestFormProps, FormButton, HighlightAvatar } from '../components'; +import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus'; +import { formatIsoDateTime, formatLocalDateTime } from './TimeFormat'; +import { NTPStatus, Time } from './types'; +import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; +import { TIME_ENDPOINT } from '../api'; + +type NTPStatusFormProps = RestFormProps & WithTheme & AuthenticatedContextProps; + +interface NTPStatusFormState { + settingTime: boolean; + localTime: string; + processing: boolean; +} + +class NTPStatusForm extends Component { + + constructor(props: NTPStatusFormProps) { + super(props); + this.state = { + settingTime: false, + localTime: '', + processing: false + }; + } + + updateLocalTime = (event: React.ChangeEvent) => { + this.setState({ localTime: event.target.value }); + } + + openSetTime = () => { + this.setState({ localTime: formatLocalDateTime(moment()), settingTime: true, }); + } + + closeSetTime = () => { + this.setState({ settingTime: false }); + } + + createAdjustedTime = (): Time => { + const currentLocalTime = moment(this.props.data.time_local); + const newLocalTime = moment(this.state.localTime); + newLocalTime.subtract(currentLocalTime.utcOffset()) + newLocalTime.milliseconds(0); + newLocalTime.utc(); + return { + time_utc: newLocalTime.format() + } + } + + configureTime = () => { + this.setState({ processing: true }); + redirectingAuthorizedFetch(TIME_ENDPOINT, + { + method: 'POST', + body: JSON.stringify(this.createAdjustedTime()), + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => { + if (response.status === 200) { + this.props.enqueueSnackbar("Time set successfully", { variant: 'success' }); + this.setState({ processing: false, settingTime: false }, this.props.loadData); + } else { + throw Error("Error setting time, status code: " + response.status); + } + }) + .catch(error => { + this.props.enqueueSnackbar(error.message || "Problem setting the time", { variant: 'error' }); + this.setState({ processing: false, settingTime: false }); + }); + } + + renderSetTimeDialog() { + return ( + + Set Time + + Enter local date and time below to set the device's time. + + + + + + + + ) + } + + render() { + const { data, theme } = this.props + const me = this.props.authenticatedContext.me; + return ( + + + + + + + + + + + + {isNtpActive(data) && ( + + + + + + + + + + + + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } variant="contained" color="secondary" onClick={this.props.loadData}> + Refresh + + + {me.admin && !isNtpActive(data) && ( + + + + )} + + {this.renderSetTimeDialog()} + + ); + } +} + +export default withAuthenticatedContext(withTheme(NTPStatusForm)); diff --git a/interface/src/ntp/NetworkTime.tsx b/interface/src/ntp/NetworkTime.tsx new file mode 100644 index 0000000..ebefb6e --- /dev/null +++ b/interface/src/ntp/NetworkTime.tsx @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { withAuthenticatedContext, AuthenticatedContextProps, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; + +import NTPStatusController from './NTPStatusController'; +import NTPSettingsController from './NTPSettingsController'; + +type NetworkTimeProps = AuthenticatedContextProps & RouteComponentProps; + +class NetworkTime extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + const { authenticatedContext } = this.props; + return ( + + + + + + + + + + + + ) + } + +} + +export default withAuthenticatedContext(NetworkTime) diff --git a/interface/src/ntp/TZ.tsx b/interface/src/ntp/TZ.tsx new file mode 100644 index 0000000..4e56e9d --- /dev/null +++ b/interface/src/ntp/TZ.tsx @@ -0,0 +1,479 @@ +import React from 'react'; +import MenuItem from '@material-ui/core/MenuItem'; + +type TimeZones = { + [name: string]: string +}; + +export const TIME_ZONES: TimeZones = { + "Africa/Abidjan": "GMT0", + "Africa/Accra": "GMT0", + "Africa/Addis_Ababa": "EAT-3", + "Africa/Algiers": "CET-1", + "Africa/Asmara": "EAT-3", + "Africa/Bamako": "GMT0", + "Africa/Bangui": "WAT-1", + "Africa/Banjul": "GMT0", + "Africa/Bissau": "GMT0", + "Africa/Blantyre": "CAT-2", + "Africa/Brazzaville": "WAT-1", + "Africa/Bujumbura": "CAT-2", + "Africa/Cairo": "EET-2", + "Africa/Casablanca": "UNK-1", + "Africa/Ceuta": "CET-1CEST,M3.5.0,M10.5.0/3", + "Africa/Conakry": "GMT0", + "Africa/Dakar": "GMT0", + "Africa/Dar_es_Salaam": "EAT-3", + "Africa/Djibouti": "EAT-3", + "Africa/Douala": "WAT-1", + "Africa/El_Aaiun": "UNK-1", + "Africa/Freetown": "GMT0", + "Africa/Gaborone": "CAT-2", + "Africa/Harare": "CAT-2", + "Africa/Johannesburg": "SAST-2", + "Africa/Juba": "EAT-3", + "Africa/Kampala": "EAT-3", + "Africa/Khartoum": "CAT-2", + "Africa/Kigali": "CAT-2", + "Africa/Kinshasa": "WAT-1", + "Africa/Lagos": "WAT-1", + "Africa/Libreville": "WAT-1", + "Africa/Lome": "GMT0", + "Africa/Luanda": "WAT-1", + "Africa/Lubumbashi": "CAT-2", + "Africa/Lusaka": "CAT-2", + "Africa/Malabo": "WAT-1", + "Africa/Maputo": "CAT-2", + "Africa/Maseru": "SAST-2", + "Africa/Mbabane": "SAST-2", + "Africa/Mogadishu": "EAT-3", + "Africa/Monrovia": "GMT0", + "Africa/Nairobi": "EAT-3", + "Africa/Ndjamena": "WAT-1", + "Africa/Niamey": "WAT-1", + "Africa/Nouakchott": "GMT0", + "Africa/Ouagadougou": "GMT0", + "Africa/Porto-Novo": "WAT-1", + "Africa/Sao_Tome": "GMT0", + "Africa/Tripoli": "EET-2", + "Africa/Tunis": "CET-1", + "Africa/Windhoek": "CAT-2", + "America/Adak": "HST10HDT,M3.2.0,M11.1.0", + "America/Anchorage": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Anguilla": "AST4", + "America/Antigua": "AST4", + "America/Araguaina": "UNK3", + "America/Argentina/Buenos_Aires": "UNK3", + "America/Argentina/Catamarca": "UNK3", + "America/Argentina/Cordoba": "UNK3", + "America/Argentina/Jujuy": "UNK3", + "America/Argentina/La_Rioja": "UNK3", + "America/Argentina/Mendoza": "UNK3", + "America/Argentina/Rio_Gallegos": "UNK3", + "America/Argentina/Salta": "UNK3", + "America/Argentina/San_Juan": "UNK3", + "America/Argentina/San_Luis": "UNK3", + "America/Argentina/Tucuman": "UNK3", + "America/Argentina/Ushuaia": "UNK3", + "America/Aruba": "AST4", + "America/Asuncion": "UNK4UNK,M10.1.0/0,M3.4.0/0", + "America/Atikokan": "EST5", + "America/Bahia": "UNK3", + "America/Bahia_Banderas": "CST6CDT,M4.1.0,M10.5.0", + "America/Barbados": "AST4", + "America/Belem": "UNK3", + "America/Belize": "CST6", + "America/Blanc-Sablon": "AST4", + "America/Boa_Vista": "UNK4", + "America/Bogota": "UNK5", + "America/Boise": "MST7MDT,M3.2.0,M11.1.0", + "America/Cambridge_Bay": "MST7MDT,M3.2.0,M11.1.0", + "America/Campo_Grande": "UNK4", + "America/Cancun": "EST5", + "America/Caracas": "UNK4", + "America/Cayenne": "UNK3", + "America/Cayman": "EST5", + "America/Chicago": "CST6CDT,M3.2.0,M11.1.0", + "America/Chihuahua": "MST7MDT,M4.1.0,M10.5.0", + "America/Costa_Rica": "CST6", + "America/Creston": "MST7", + "America/Cuiaba": "UNK4", + "America/Curacao": "AST4", + "America/Danmarkshavn": "GMT0", + "America/Dawson": "MST7", + "America/Dawson_Creek": "MST7", + "America/Denver": "MST7MDT,M3.2.0,M11.1.0", + "America/Detroit": "EST5EDT,M3.2.0,M11.1.0", + "America/Dominica": "AST4", + "America/Edmonton": "MST7MDT,M3.2.0,M11.1.0", + "America/Eirunepe": "UNK5", + "America/El_Salvador": "CST6", + "America/Fort_Nelson": "MST7", + "America/Fortaleza": "UNK3", + "America/Glace_Bay": "AST4ADT,M3.2.0,M11.1.0", + "America/Godthab": "UNK3UNK,M3.5.0/-2,M10.5.0/-1", + "America/Goose_Bay": "AST4ADT,M3.2.0,M11.1.0", + "America/Grand_Turk": "EST5EDT,M3.2.0,M11.1.0", + "America/Grenada": "AST4", + "America/Guadeloupe": "AST4", + "America/Guatemala": "CST6", + "America/Guayaquil": "UNK5", + "America/Guyana": "UNK4", + "America/Halifax": "AST4ADT,M3.2.0,M11.1.0", + "America/Havana": "CST5CDT,M3.2.0/0,M11.1.0/1", + "America/Hermosillo": "MST7", + "America/Indiana/Indianapolis": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Knox": "CST6CDT,M3.2.0,M11.1.0", + "America/Indiana/Marengo": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Petersburg": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Tell_City": "CST6CDT,M3.2.0,M11.1.0", + "America/Indiana/Vevay": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Vincennes": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Winamac": "EST5EDT,M3.2.0,M11.1.0", + "America/Inuvik": "MST7MDT,M3.2.0,M11.1.0", + "America/Iqaluit": "EST5EDT,M3.2.0,M11.1.0", + "America/Jamaica": "EST5", + "America/Juneau": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Kentucky/Louisville": "EST5EDT,M3.2.0,M11.1.0", + "America/Kentucky/Monticello": "EST5EDT,M3.2.0,M11.1.0", + "America/Kralendijk": "AST4", + "America/La_Paz": "UNK4", + "America/Lima": "UNK5", + "America/Los_Angeles": "PST8PDT,M3.2.0,M11.1.0", + "America/Lower_Princes": "AST4", + "America/Maceio": "UNK3", + "America/Managua": "CST6", + "America/Manaus": "UNK4", + "America/Marigot": "AST4", + "America/Martinique": "AST4", + "America/Matamoros": "CST6CDT,M3.2.0,M11.1.0", + "America/Mazatlan": "MST7MDT,M4.1.0,M10.5.0", + "America/Menominee": "CST6CDT,M3.2.0,M11.1.0", + "America/Merida": "CST6CDT,M4.1.0,M10.5.0", + "America/Metlakatla": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Mexico_City": "CST6CDT,M4.1.0,M10.5.0", + "America/Miquelon": "UNK3UNK,M3.2.0,M11.1.0", + "America/Moncton": "AST4ADT,M3.2.0,M11.1.0", + "America/Monterrey": "CST6CDT,M4.1.0,M10.5.0", + "America/Montevideo": "UNK3", + "America/Montreal": "EST5EDT,M3.2.0,M11.1.0", + "America/Montserrat": "AST4", + "America/Nassau": "EST5EDT,M3.2.0,M11.1.0", + "America/New_York": "EST5EDT,M3.2.0,M11.1.0", + "America/Nipigon": "EST5EDT,M3.2.0,M11.1.0", + "America/Nome": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Noronha": "UNK2", + "America/North_Dakota/Beulah": "CST6CDT,M3.2.0,M11.1.0", + "America/North_Dakota/Center": "CST6CDT,M3.2.0,M11.1.0", + "America/North_Dakota/New_Salem": "CST6CDT,M3.2.0,M11.1.0", + "America/Ojinaga": "MST7MDT,M3.2.0,M11.1.0", + "America/Panama": "EST5", + "America/Pangnirtung": "EST5EDT,M3.2.0,M11.1.0", + "America/Paramaribo": "UNK3", + "America/Phoenix": "MST7", + "America/Port-au-Prince": "EST5EDT,M3.2.0,M11.1.0", + "America/Port_of_Spain": "AST4", + "America/Porto_Velho": "UNK4", + "America/Puerto_Rico": "AST4", + "America/Punta_Arenas": "UNK3", + "America/Rainy_River": "CST6CDT,M3.2.0,M11.1.0", + "America/Rankin_Inlet": "CST6CDT,M3.2.0,M11.1.0", + "America/Recife": "UNK3", + "America/Regina": "CST6", + "America/Resolute": "CST6CDT,M3.2.0,M11.1.0", + "America/Rio_Branco": "UNK5", + "America/Santarem": "UNK3", + "America/Santiago": "UNK4UNK,M9.1.6/24,M4.1.6/24", + "America/Santo_Domingo": "AST4", + "America/Sao_Paulo": "UNK3", + "America/Scoresbysund": "UNK1UNK,M3.5.0/0,M10.5.0/1", + "America/Sitka": "AKST9AKDT,M3.2.0,M11.1.0", + "America/St_Barthelemy": "AST4", + "America/St_Johns": "NST3:30NDT,M3.2.0,M11.1.0", + "America/St_Kitts": "AST4", + "America/St_Lucia": "AST4", + "America/St_Thomas": "AST4", + "America/St_Vincent": "AST4", + "America/Swift_Current": "CST6", + "America/Tegucigalpa": "CST6", + "America/Thule": "AST4ADT,M3.2.0,M11.1.0", + "America/Thunder_Bay": "EST5EDT,M3.2.0,M11.1.0", + "America/Tijuana": "PST8PDT,M3.2.0,M11.1.0", + "America/Toronto": "EST5EDT,M3.2.0,M11.1.0", + "America/Tortola": "AST4", + "America/Vancouver": "PST8PDT,M3.2.0,M11.1.0", + "America/Whitehorse": "MST7", + "America/Winnipeg": "CST6CDT,M3.2.0,M11.1.0", + "America/Yakutat": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Yellowknife": "MST7MDT,M3.2.0,M11.1.0", + "Antarctica/Casey": "UNK-8", + "Antarctica/Davis": "UNK-7", + "Antarctica/DumontDUrville": "UNK-10", + "Antarctica/Macquarie": "UNK-11", + "Antarctica/Mawson": "UNK-5", + "Antarctica/McMurdo": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "Antarctica/Palmer": "UNK3", + "Antarctica/Rothera": "UNK3", + "Antarctica/Syowa": "UNK-3", + "Antarctica/Troll": "UNK0UNK-2,M3.5.0/1,M10.5.0/3", + "Antarctica/Vostok": "UNK-6", + "Arctic/Longyearbyen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Aden": "UNK-3", + "Asia/Almaty": "UNK-6", + "Asia/Amman": "EET-2EEST,M3.5.4/24,M10.5.5/1", + "Asia/Anadyr": "UNK-12", + "Asia/Aqtau": "UNK-5", + "Asia/Aqtobe": "UNK-5", + "Asia/Ashgabat": "UNK-5", + "Asia/Atyrau": "UNK-5", + "Asia/Baghdad": "UNK-3", + "Asia/Bahrain": "UNK-3", + "Asia/Baku": "UNK-4", + "Asia/Bangkok": "UNK-7", + "Asia/Barnaul": "UNK-7", + "Asia/Beirut": "EET-2EEST,M3.5.0/0,M10.5.0/0", + "Asia/Bishkek": "UNK-6", + "Asia/Brunei": "UNK-8", + "Asia/Chita": "UNK-9", + "Asia/Choibalsan": "UNK-8", + "Asia/Colombo": "UNK-5:30", + "Asia/Damascus": "EET-2EEST,M3.5.5/0,M10.5.5/0", + "Asia/Dhaka": "UNK-6", + "Asia/Dili": "UNK-9", + "Asia/Dubai": "UNK-4", + "Asia/Dushanbe": "UNK-5", + "Asia/Famagusta": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Gaza": "EET-2EEST,M3.5.5/0,M10.5.6/1", + "Asia/Hebron": "EET-2EEST,M3.5.5/0,M10.5.6/1", + "Asia/Ho_Chi_Minh": "UNK-7", + "Asia/Hong_Kong": "HKT-8", + "Asia/Hovd": "UNK-7", + "Asia/Irkutsk": "UNK-8", + "Asia/Jakarta": "WIB-7", + "Asia/Jayapura": "WIT-9", + "Asia/Jerusalem": "IST-2IDT,M3.4.4/26,M10.5.0", + "Asia/Kabul": "UNK-4:30", + "Asia/Kamchatka": "UNK-12", + "Asia/Karachi": "PKT-5", + "Asia/Kathmandu": "UNK-5:45", + "Asia/Khandyga": "UNK-9", + "Asia/Kolkata": "IST-5:30", + "Asia/Krasnoyarsk": "UNK-7", + "Asia/Kuala_Lumpur": "UNK-8", + "Asia/Kuching": "UNK-8", + "Asia/Kuwait": "UNK-3", + "Asia/Macau": "CST-8", + "Asia/Magadan": "UNK-11", + "Asia/Makassar": "WITA-8", + "Asia/Manila": "PST-8", + "Asia/Muscat": "UNK-4", + "Asia/Nicosia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Novokuznetsk": "UNK-7", + "Asia/Novosibirsk": "UNK-7", + "Asia/Omsk": "UNK-6", + "Asia/Oral": "UNK-5", + "Asia/Phnom_Penh": "UNK-7", + "Asia/Pontianak": "WIB-7", + "Asia/Pyongyang": "KST-9", + "Asia/Qatar": "UNK-3", + "Asia/Qyzylorda": "UNK-5", + "Asia/Riyadh": "UNK-3", + "Asia/Sakhalin": "UNK-11", + "Asia/Samarkand": "UNK-5", + "Asia/Seoul": "KST-9", + "Asia/Shanghai": "CST-8", + "Asia/Singapore": "UNK-8", + "Asia/Srednekolymsk": "UNK-11", + "Asia/Taipei": "CST-8", + "Asia/Tashkent": "UNK-5", + "Asia/Tbilisi": "UNK-4", + "Asia/Tehran": "UNK-3:30UNK,J79/24,J263/24", + "Asia/Thimphu": "UNK-6", + "Asia/Tokyo": "JST-9", + "Asia/Tomsk": "UNK-7", + "Asia/Ulaanbaatar": "UNK-8", + "Asia/Urumqi": "UNK-6", + "Asia/Ust-Nera": "UNK-10", + "Asia/Vientiane": "UNK-7", + "Asia/Vladivostok": "UNK-10", + "Asia/Yakutsk": "UNK-9", + "Asia/Yangon": "UNK-6:30", + "Asia/Yekaterinburg": "UNK-5", + "Asia/Yerevan": "UNK-4", + "Atlantic/Azores": "UNK1UNK,M3.5.0/0,M10.5.0/1", + "Atlantic/Bermuda": "AST4ADT,M3.2.0,M11.1.0", + "Atlantic/Canary": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Cape_Verde": "UNK1", + "Atlantic/Faroe": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Madeira": "WET0WEST,M3.5.0/1,M10.5.0", + "Atlantic/Reykjavik": "GMT0", + "Atlantic/South_Georgia": "UNK2", + "Atlantic/St_Helena": "GMT0", + "Atlantic/Stanley": "UNK3", + "Australia/Adelaide": "ACST-9:30ACDT,M10.1.0,M4.1.0/3", + "Australia/Brisbane": "AEST-10", + "Australia/Broken_Hill": "ACST-9:30ACDT,M10.1.0,M4.1.0/3", + "Australia/Currie": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Darwin": "ACST-9:30", + "Australia/Eucla": "UNK-8:45", + "Australia/Hobart": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Lindeman": "AEST-10", + "Australia/Lord_Howe": "UNK-10:30UNK-11,M10.1.0,M4.1.0", + "Australia/Melbourne": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Australia/Perth": "AWST-8", + "Australia/Sydney": "AEST-10AEDT,M10.1.0,M4.1.0/3", + "Etc/GMT": "GMT0", + "Etc/GMT+0": "GMT0", + "Etc/GMT+1": "UNK1", + "Etc/GMT+10": "UNK10", + "Etc/GMT+11": "UNK11", + "Etc/GMT+12": "UNK12", + "Etc/GMT+2": "UNK2", + "Etc/GMT+3": "UNK3", + "Etc/GMT+4": "UNK4", + "Etc/GMT+5": "UNK5", + "Etc/GMT+6": "UNK6", + "Etc/GMT+7": "UNK7", + "Etc/GMT+8": "UNK8", + "Etc/GMT+9": "UNK9", + "Etc/GMT-0": "GMT0", + "Etc/GMT-1": "UNK-1", + "Etc/GMT-10": "UNK-10", + "Etc/GMT-11": "UNK-11", + "Etc/GMT-12": "UNK-12", + "Etc/GMT-13": "UNK-13", + "Etc/GMT-14": "UNK-14", + "Etc/GMT-2": "UNK-2", + "Etc/GMT-3": "UNK-3", + "Etc/GMT-4": "UNK-4", + "Etc/GMT-5": "UNK-5", + "Etc/GMT-6": "UNK-6", + "Etc/GMT-7": "UNK-7", + "Etc/GMT-8": "UNK-8", + "Etc/GMT-9": "UNK-9", + "Etc/GMT0": "GMT0", + "Etc/Greenwich": "GMT0", + "Etc/UCT": "UTC0", + "Etc/UTC": "UTC0", + "Etc/Universal": "UTC0", + "Etc/Zulu": "UTC0", + "Europe/Amsterdam": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Andorra": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Astrakhan": "UNK-4", + "Europe/Athens": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Belgrade": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Berlin": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Bratislava": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Brussels": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Bucharest": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Budapest": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Busingen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Chisinau": "EET-2EEST,M3.5.0,M10.5.0/3", + "Europe/Copenhagen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Dublin": "IST-1GMT0,M10.5.0,M3.5.0/1", + "Europe/Gibraltar": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Guernsey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Helsinki": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Isle_of_Man": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Istanbul": "UNK-3", + "Europe/Jersey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Kaliningrad": "EET-2", + "Europe/Kiev": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Kirov": "UNK-3", + "Europe/Lisbon": "WET0WEST,M3.5.0/1,M10.5.0", + "Europe/Ljubljana": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/London": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Luxembourg": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Madrid": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Malta": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Mariehamn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Minsk": "UNK-3", + "Europe/Monaco": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Moscow": "MSK-3", + "Europe/Oslo": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Paris": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Podgorica": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Prague": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Riga": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Rome": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Samara": "UNK-4", + "Europe/San_Marino": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Sarajevo": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Saratov": "UNK-4", + "Europe/Simferopol": "MSK-3", + "Europe/Skopje": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Sofia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Stockholm": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Tallinn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Tirane": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Ulyanovsk": "UNK-4", + "Europe/Uzhgorod": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Vaduz": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vatican": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vienna": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Vilnius": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Volgograd": "UNK-4", + "Europe/Warsaw": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Zagreb": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Zaporozhye": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Zurich": "CET-1CEST,M3.5.0,M10.5.0/3", + "Indian/Antananarivo": "EAT-3", + "Indian/Chagos": "UNK-6", + "Indian/Christmas": "UNK-7", + "Indian/Cocos": "UNK-6:30", + "Indian/Comoro": "EAT-3", + "Indian/Kerguelen": "UNK-5", + "Indian/Mahe": "UNK-4", + "Indian/Maldives": "UNK-5", + "Indian/Mauritius": "UNK-4", + "Indian/Mayotte": "EAT-3", + "Indian/Reunion": "UNK-4", + "Pacific/Apia": "UNK-13UNK,M9.5.0/3,M4.1.0/4", + "Pacific/Auckland": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "Pacific/Bougainville": "UNK-11", + "Pacific/Chatham": "UNK-12:45UNK,M9.5.0/2:45,M4.1.0/3:45", + "Pacific/Chuuk": "UNK-10", + "Pacific/Easter": "UNK6UNK,M9.1.6/22,M4.1.6/22", + "Pacific/Efate": "UNK-11", + "Pacific/Enderbury": "UNK-13", + "Pacific/Fakaofo": "UNK-13", + "Pacific/Fiji": "UNK-12UNK,M11.2.0,M1.2.3/99", + "Pacific/Funafuti": "UNK-12", + "Pacific/Galapagos": "UNK6", + "Pacific/Gambier": "UNK9", + "Pacific/Guadalcanal": "UNK-11", + "Pacific/Guam": "ChST-10", + "Pacific/Honolulu": "HST10", + "Pacific/Kiritimati": "UNK-14", + "Pacific/Kosrae": "UNK-11", + "Pacific/Kwajalein": "UNK-12", + "Pacific/Majuro": "UNK-12", + "Pacific/Marquesas": "UNK9:30", + "Pacific/Midway": "SST11", + "Pacific/Nauru": "UNK-12", + "Pacific/Niue": "UNK11", + "Pacific/Norfolk": "UNK-11UNK,M10.1.0,M4.1.0/3", + "Pacific/Noumea": "UNK-11", + "Pacific/Pago_Pago": "SST11", + "Pacific/Palau": "UNK-9", + "Pacific/Pitcairn": "UNK8", + "Pacific/Pohnpei": "UNK-11", + "Pacific/Port_Moresby": "UNK-10", + "Pacific/Rarotonga": "UNK10", + "Pacific/Saipan": "ChST-10", + "Pacific/Tahiti": "UNK10", + "Pacific/Tarawa": "UNK-12", + "Pacific/Tongatapu": "UNK-13", + "Pacific/Wake": "UNK-12", + "Pacific/Wallis": "UNK-12" +} + +export function selectedTimeZone(label: string, format: string) { + return TIME_ZONES[label] === format ? label : undefined; +} + +export function timeZoneSelectItems() { + return Object.keys(TIME_ZONES).map(label => ( + {label} + )); +} diff --git a/interface/src/ntp/TimeFormat.ts b/interface/src/ntp/TimeFormat.ts new file mode 100644 index 0000000..7e0bb82 --- /dev/null +++ b/interface/src/ntp/TimeFormat.ts @@ -0,0 +1,5 @@ +import moment, { Moment } from 'moment'; + +export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss'); + +export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm'); diff --git a/interface/src/ntp/types.ts b/interface/src/ntp/types.ts new file mode 100644 index 0000000..a266d12 --- /dev/null +++ b/interface/src/ntp/types.ts @@ -0,0 +1,23 @@ +export enum NTPSyncStatus { + NTP_INACTIVE = 0, + NTP_ACTIVE = 1 +} + +export interface NTPStatus { + status: NTPSyncStatus; + time_utc: string; + time_local: string; + server: string; + uptime: number; +} + +export interface NTPSettings { + enabled: boolean; + server: string; + tz_label: string; + tz_format: string; +} + +export interface Time { + time_utc: string; +} diff --git a/interface/src/project/DemoInformation.tsx b/interface/src/project/DemoInformation.tsx new file mode 100644 index 0000000..2b9967b --- /dev/null +++ b/interface/src/project/DemoInformation.tsx @@ -0,0 +1,77 @@ +import React, { Component } from 'react'; +import { Typography, Box, List, ListItem, ListItemText } from '@material-ui/core'; +import { SectionContent } from '../components'; + +class DemoInformation extends Component { + + render() { + return ( + + + This simple demo project allows you to control the built-in LED. + It demonstrates how the esp8266-react framework may be extended for your own IoT project. + + + It is recommended that you keep your project interface code under the project directory. + This serves to isolate your project code from the from the rest of the user interface which should + simplify merges should you wish to update your project with future framework changes. + + + The demo project interface code is stored in the 'interface/src/project' directory: + + + + + + + + + + + + + + + + + + + + + + + + + + + See the project README for a full description of the demo project. + + + + ) + } + +} + +export default DemoInformation; diff --git a/interface/src/project/DemoProject.tsx b/interface/src/project/DemoProject.tsx new file mode 100644 index 0000000..74f25e5 --- /dev/null +++ b/interface/src/project/DemoProject.tsx @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { PROJECT_PATH } from '../api'; +import { MenuAppBar } from '../components'; +import { AuthenticatedRoute } from '../authentication'; + +import DemoInformation from './DemoInformation'; +import LightStateRestController from './LightStateRestController'; +import LightStateWebSocketController from './LightStateWebSocketController'; +import LightMqttSettingsController from './LightMqttSettingsController'; + +class DemoProject extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + return ( + + + + + + + + + + + + + + + + ) + } + +} + +export default DemoProject; diff --git a/interface/src/project/LightMqttSettingsController.tsx b/interface/src/project/LightMqttSettingsController.tsx new file mode 100644 index 0000000..7e4db5c --- /dev/null +++ b/interface/src/project/LightMqttSettingsController.tsx @@ -0,0 +1,90 @@ +import React, { Component } from 'react'; +import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator'; + +import { Typography, Box } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { ENDPOINT_ROOT } from '../api'; +import { restController, RestControllerProps, RestFormLoader, RestFormProps, FormActions, FormButton, SectionContent } from '../components'; + +import { LightMqttSettings } from './types'; + +export const LIGHT_BROKER_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "brokerSettings"; + +type LightMqttSettingsControllerProps = RestControllerProps; + +class LightMqttSettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + ( + + )} + /> + + ) + } + +} + +export default restController(LIGHT_BROKER_SETTINGS_ENDPOINT, LightMqttSettingsController); + +type LightMqttSettingsControllerFormProps = RestFormProps; + +function LightMqttSettingsControllerForm(props: LightMqttSettingsControllerFormProps) { + const { data, saveData, handleValueChange } = props; + return ( + + + + The LED is controllable via MQTT with the demo project designed to work with Home Assistant's auto discovery feature. + + + + + + + } variant="contained" color="primary" type="submit"> + Save + + + + ); +} diff --git a/interface/src/project/LightStateRestController.tsx b/interface/src/project/LightStateRestController.tsx new file mode 100644 index 0000000..764ce35 --- /dev/null +++ b/interface/src/project/LightStateRestController.tsx @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; + +import { Typography, Box, Checkbox } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { ENDPOINT_ROOT } from '../api'; +import { restController, RestControllerProps, RestFormLoader, RestFormProps, FormActions, FormButton, SectionContent, BlockFormControlLabel } from '../components'; + +import { LightState } from './types'; + +export const LIGHT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "lightState"; + +type LightStateRestControllerProps = RestControllerProps; + +class LightStateRestController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + ( + + )} + /> + + ) + } + +} + +export default restController(LIGHT_SETTINGS_ENDPOINT, LightStateRestController); + +type LightStateRestControllerFormProps = RestFormProps; + +function LightStateRestControllerForm(props: LightStateRestControllerFormProps) { + const { data, saveData, handleValueChange } = props; + return ( + + + + The form below controls the LED via the RESTful service exposed by the ESP device. + + + + } + label="LED State?" + /> + + } variant="contained" color="primary" type="submit"> + Save + + + + ); +} diff --git a/interface/src/project/LightStateWebSocketController.tsx b/interface/src/project/LightStateWebSocketController.tsx new file mode 100644 index 0000000..a0b99a2 --- /dev/null +++ b/interface/src/project/LightStateWebSocketController.tsx @@ -0,0 +1,62 @@ +import React, { Component } from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; + +import { Typography, Box, Switch } from '@material-ui/core'; +import { WEB_SOCKET_ROOT } from '../api'; +import { WebSocketControllerProps, WebSocketFormLoader, WebSocketFormProps, webSocketController } from '../components'; +import { SectionContent, BlockFormControlLabel } from '../components'; + +import { LightState } from './types'; + +export const LIGHT_SETTINGS_WEBSOCKET_URL = WEB_SOCKET_ROOT + "lightState"; + +type LightStateWebSocketControllerProps = WebSocketControllerProps; + +class LightStateWebSocketController extends Component { + + render() { + return ( + + ( + + )} + /> + + ) + } + +} + +export default webSocketController(LIGHT_SETTINGS_WEBSOCKET_URL, 100, LightStateWebSocketController); + +type LightStateWebSocketControllerFormProps = WebSocketFormProps; + +function LightStateWebSocketControllerForm(props: LightStateWebSocketControllerFormProps) { + const { data, saveData, setData } = props; + + const changeLedOn = (event: React.ChangeEvent) => { + setData({ led_on: event.target.checked }, saveData); + } + + return ( + + + + The switch below controls the LED via the WebSocket. It will automatically update whenever the LED state changes. + + + + } + label="LED State?" + /> + + ); +} diff --git a/interface/src/project/ProjectMenu.tsx b/interface/src/project/ProjectMenu.tsx new file mode 100644 index 0000000..b7d2739 --- /dev/null +++ b/interface/src/project/ProjectMenu.tsx @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import { Link, withRouter, RouteComponentProps } from 'react-router-dom'; + +import {List, ListItem, ListItemIcon, ListItemText} from '@material-ui/core'; +import SettingsRemoteIcon from '@material-ui/icons/SettingsRemote'; + +import { PROJECT_PATH } from '../api'; + +class ProjectMenu extends Component { + + render() { + const path = this.props.match.url; + return ( + + + + + + + + + ) + } + +} + +export default withRouter(ProjectMenu); diff --git a/interface/src/project/ProjectRouting.tsx b/interface/src/project/ProjectRouting.tsx new file mode 100644 index 0000000..fc378e6 --- /dev/null +++ b/interface/src/project/ProjectRouting.tsx @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; +import { Redirect, Switch } from 'react-router'; + +import { PROJECT_PATH } from '../api'; +import { AuthenticatedRoute } from '../authentication'; + +import DemoProject from './DemoProject'; + +class ProjectRouting extends Component { + + render() { + return ( + + { + /* + * Add your project page routing below. + */ + } + + { + /* + * The redirect below caters for the default project route and redirecting invalid paths. + * The "to" property must match one of the routes above for this to work correctly. + */ + } + + + ) + } + +} + +export default ProjectRouting; diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts new file mode 100644 index 0000000..3221255 --- /dev/null +++ b/interface/src/project/types.ts @@ -0,0 +1,9 @@ +export interface LightState { + led_on: boolean; +} + +export interface LightMqttSettings { + unique_id : string; + name: string; + mqtt_path : string; +} diff --git a/interface/src/react-app-env.d.ts b/interface/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/interface/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/interface/src/security/ManageUsersController.tsx b/interface/src/security/ManageUsersController.tsx new file mode 100644 index 0000000..ccd3cde --- /dev/null +++ b/interface/src/security/ManageUsersController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { SECURITY_SETTINGS_ENDPOINT } from '../api'; + +import ManageUsersForm from './ManageUsersForm'; +import { SecuritySettings } from './types'; + +type ManageUsersControllerProps = RestControllerProps; + +class ManageUsersController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ) + } + +} + +export default restController(SECURITY_SETTINGS_ENDPOINT, ManageUsersController); diff --git a/interface/src/security/ManageUsersForm.tsx b/interface/src/security/ManageUsersForm.tsx new file mode 100644 index 0000000..b8c63b7 --- /dev/null +++ b/interface/src/security/ManageUsersForm.tsx @@ -0,0 +1,184 @@ +import React, { Fragment } from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; + +import { Table, TableBody, TableCell, TableHead, TableFooter, TableRow, withWidth, WithWidthProps, isWidthDown } from '@material-ui/core'; +import { Box, Button, Typography, } from '@material-ui/core'; + +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; +import CloseIcon from '@material-ui/icons/Close'; +import CheckIcon from '@material-ui/icons/Check'; +import IconButton from '@material-ui/core/IconButton'; +import SaveIcon from '@material-ui/icons/Save'; +import PersonAddIcon from '@material-ui/icons/PersonAdd'; + +import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; +import { RestFormProps, FormActions, FormButton, extractEventValue } from '../components'; + +import UserForm from './UserForm'; +import { SecuritySettings, User } from './types'; + +function compareUsers(a: User, b: User) { + if (a.username < b.username) { + return -1; + } + if (a.username > b.username) { + return 1; + } + return 0; +} + +type ManageUsersFormProps = RestFormProps & AuthenticatedContextProps & WithWidthProps; + +type ManageUsersFormState = { + creating: boolean; + user?: User; +} + +class ManageUsersForm extends React.Component { + + state: ManageUsersFormState = { + creating: false + }; + + createUser = () => { + this.setState({ + creating: true, + user: { + username: "", + password: "", + admin: true + } + }); + }; + + uniqueUsername = (username: string) => { + return !this.props.data.users.find(u => u.username === username); + } + + noAdminConfigured = () => { + return !this.props.data.users.find(u => u.admin); + } + + removeUser = (user: User) => { + const { data } = this.props; + const users = data.users.filter(u => u.username !== user.username); + this.props.setData({ ...data, users }); + } + + startEditingUser = (user: User) => { + this.setState({ + creating: false, + user + }); + }; + + cancelEditingUser = () => { + this.setState({ + user: undefined + }); + } + + doneEditingUser = () => { + const { user } = this.state; + if (user) { + const { data } = this.props; + const users = data.users.filter(u => u.username !== user.username); + users.push(user); + this.props.setData({ ...data, users }); + this.setState({ + user: undefined + }); + } + }; + + handleUserValueChange = (name: keyof User) => (event: React.ChangeEvent) => { + this.setState({ user: { ...this.state.user!, [name]: extractEventValue(event) } }); + }; + + onSubmit = () => { + this.props.saveData(); + this.props.authenticatedContext.refresh(); + } + + render() { + const { width, data } = this.props; + const { user, creating } = this.state; + return ( + + + + + + Username + Admin? + + + + + {data.users.sort(compareUsers).map(user => ( + + + {user.username} + + + { + user.admin ? : + } + + + this.removeUser(user)}> + + + this.startEditingUser(user)}> + + + + + ))} + + + + + + + + + +
+ { + this.noAdminConfigured() && + ( + + + You must have at least one admin user configured. + + + ) + } + + } variant="contained" color="primary" type="submit" disabled={this.noAdminConfigured()}> + Save + + +
+ { + user && + + } +
+ ); + } + +} + +export default withAuthenticatedContext(withWidth()(ManageUsersForm)); diff --git a/interface/src/security/Security.tsx b/interface/src/security/Security.tsx new file mode 100644 index 0000000..4e99769 --- /dev/null +++ b/interface/src/security/Security.tsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { AuthenticatedContextProps, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; + +import ManageUsersController from './ManageUsersController'; +import SecuritySettingsController from './SecuritySettingsController'; + +type SecurityProps = AuthenticatedContextProps & RouteComponentProps; + +class Security extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + return ( + + + + + + + + + + + + ) + } +} + +export default Security; diff --git a/interface/src/security/SecuritySettingsController.tsx b/interface/src/security/SecuritySettingsController.tsx new file mode 100644 index 0000000..d42328a --- /dev/null +++ b/interface/src/security/SecuritySettingsController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { SECURITY_SETTINGS_ENDPOINT } from '../api'; + +import SecuritySettingsForm from './SecuritySettingsForm'; +import { SecuritySettings } from './types'; + +type SecuritySettingsControllerProps = RestControllerProps; + +class SecuritySettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(SECURITY_SETTINGS_ENDPOINT, SecuritySettingsController); diff --git a/interface/src/security/SecuritySettingsForm.tsx b/interface/src/security/SecuritySettingsForm.tsx new file mode 100644 index 0000000..6407f5f --- /dev/null +++ b/interface/src/security/SecuritySettingsForm.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; + +import { Box, Typography } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication'; +import { RestFormProps, PasswordValidator, FormActions, FormButton } from '../components'; + +import { SecuritySettings } from './types'; + +type SecuritySettingsFormProps = RestFormProps & AuthenticatedContextProps; + +class SecuritySettingsForm extends React.Component { + + onSubmit = () => { + this.props.saveData(); + this.props.authenticatedContext.refresh(); + } + + render() { + const { data, handleValueChange } = this.props; + return ( + + + + + The JWT secret is used to sign authentication tokens. If you modify the JWT Secret, all users will be signed out. + + + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } + +} + +export default withAuthenticatedContext(SecuritySettingsForm); diff --git a/interface/src/security/UserForm.tsx b/interface/src/security/UserForm.tsx new file mode 100644 index 0000000..8eefa7d --- /dev/null +++ b/interface/src/security/UserForm.tsx @@ -0,0 +1,86 @@ +import React, { RefObject } from 'react'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; + +import { Dialog, DialogTitle, DialogContent, DialogActions, Checkbox } from '@material-ui/core'; + +import { PasswordValidator, BlockFormControlLabel, FormButton } from '../components'; + +import { User } from './types'; + +interface UserFormProps { + creating: boolean; + user: User; + uniqueUsername: (value: any) => boolean; + handleValueChange: (name: keyof User) => (event: React.ChangeEvent) => void; + onDoneEditing: () => void; + onCancelEditing: () => void; +} + +class UserForm extends React.Component { + + formRef: RefObject = React.createRef(); + + componentDidMount() { + ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername); + } + + submit = () => { + this.formRef.current.submit(); + } + + render() { + const { user, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props; + return ( + + + {creating ? 'Add' : 'Modify'} User + + + + + } + label="Admin?" + /> + + + + Cancel + + + Done + + + + + ); + } +} + +export default UserForm; diff --git a/interface/src/security/types.ts b/interface/src/security/types.ts new file mode 100644 index 0000000..99d54de --- /dev/null +++ b/interface/src/security/types.ts @@ -0,0 +1,11 @@ +export interface User { + username: string; + password: string; + admin: boolean; +} + +export interface SecuritySettings { + users: User[]; + jwt_secret: string; +} + diff --git a/interface/src/serviceWorker.ts b/interface/src/serviceWorker.ts new file mode 100644 index 0000000..d5f0275 --- /dev/null +++ b/interface/src/serviceWorker.ts @@ -0,0 +1,145 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +type Config = { + onSuccess?: (registration: ServiceWorkerRegistration) => void; + onUpdate?: (registration: ServiceWorkerRegistration) => void; +}; + +export function register(config?: Config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL( + process.env.PUBLIC_URL, + window.location.href + ); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl: string, config?: Config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl: string, config?: Config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { 'Service-Worker': 'script' } + }) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/interface/src/system/OTASettingsController.tsx b/interface/src/system/OTASettingsController.tsx new file mode 100644 index 0000000..2f683b3 --- /dev/null +++ b/interface/src/system/OTASettingsController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { OTA_SETTINGS_ENDPOINT } from '../api'; + +import OTASettingsForm from './OTASettingsForm'; +import { OTASettings } from './types'; + +type OTASettingsControllerProps = RestControllerProps; + +class OTASettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(OTA_SETTINGS_ENDPOINT, OTASettingsController); diff --git a/interface/src/system/OTASettingsForm.tsx b/interface/src/system/OTASettingsForm.tsx new file mode 100644 index 0000000..c518995 --- /dev/null +++ b/interface/src/system/OTASettingsForm.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; + +import { Checkbox } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; + +import { RestFormProps, BlockFormControlLabel, PasswordValidator, FormButton, FormActions } from '../components'; +import {isIP,isHostname,or} from '../validators'; + +import { OTASettings } from './types'; + +type OTASettingsFormProps = RestFormProps; + +class OTASettingsForm extends React.Component { + + componentDidMount() { + ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname)); + } + + render() { + const { data, handleValueChange, saveData } = this.props; + return ( + + + } + label="Enable OTA Updates?" + /> + + + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } +} + +export default OTASettingsForm; diff --git a/interface/src/system/System.tsx b/interface/src/system/System.tsx new file mode 100644 index 0000000..671d5e0 --- /dev/null +++ b/interface/src/system/System.tsx @@ -0,0 +1,51 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { WithFeaturesProps, withFeatures } from '../features/FeaturesContext'; + +import { withAuthenticatedContext, AuthenticatedContextProps, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; + +import SystemStatusController from './SystemStatusController'; +import OTASettingsController from './OTASettingsController'; +import UploadFirmwareController from './UploadFirmwareController'; + +type SystemProps = AuthenticatedContextProps & RouteComponentProps & WithFeaturesProps; + +class System extends Component { + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + const { authenticatedContext, features } = this.props; + return ( + + + + {features.ota && ( + + )} + {features.upload_firmware && ( + + )} + + + + {features.ota && ( + + )} + {features.upload_firmware && ( + + )} + + + + ) + } +} + +export default withFeatures(withAuthenticatedContext(System)); diff --git a/interface/src/system/SystemStatusController.tsx b/interface/src/system/SystemStatusController.tsx new file mode 100644 index 0000000..abee0b5 --- /dev/null +++ b/interface/src/system/SystemStatusController.tsx @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { SYSTEM_STATUS_ENDPOINT } from '../api'; + +import SystemStatusForm from './SystemStatusForm'; +import { SystemStatus } from './types'; + +type SystemStatusControllerProps = RestControllerProps; + +class SystemStatusController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(SYSTEM_STATUS_ENDPOINT, SystemStatusController); diff --git a/interface/src/system/SystemStatusForm.tsx b/interface/src/system/SystemStatusForm.tsx new file mode 100644 index 0000000..751946e --- /dev/null +++ b/interface/src/system/SystemStatusForm.tsx @@ -0,0 +1,245 @@ +import React, { Component, Fragment } from 'react'; + +import { Avatar, Button, Divider, Dialog, DialogTitle, DialogContent, DialogActions, Box } from '@material-ui/core'; +import { List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; + +import DevicesIcon from '@material-ui/icons/Devices'; +import MemoryIcon from '@material-ui/icons/Memory'; +import ShowChartIcon from '@material-ui/icons/ShowChart'; +import SdStorageIcon from '@material-ui/icons/SdStorage'; +import FolderIcon from '@material-ui/icons/Folder'; +import DataUsageIcon from '@material-ui/icons/DataUsage'; +import AppsIcon from '@material-ui/icons/Apps'; +import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import SettingsBackupRestoreIcon from '@material-ui/icons/SettingsBackupRestore'; + +import { redirectingAuthorizedFetch, AuthenticatedContextProps, withAuthenticatedContext } from '../authentication'; +import { RestFormProps, FormButton, ErrorButton } from '../components'; +import { FACTORY_RESET_ENDPOINT, RESTART_ENDPOINT } from '../api'; + +import { SystemStatus, EspPlatform } from './types'; + +interface SystemStatusFormState { + confirmRestart: boolean; + confirmFactoryReset: boolean; + processing: boolean; +} + +type SystemStatusFormProps = AuthenticatedContextProps & RestFormProps; + +function formatNumber(num: number) { + return new Intl.NumberFormat().format(num); +} + +class SystemStatusForm extends Component { + + state: SystemStatusFormState = { + confirmRestart: false, + confirmFactoryReset: false, + processing: false + } + + createListItems() { + const { data } = this.props + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + (data.esp_platform === EspPlatform.ESP32 && data.psram_size > 0) && ( + + + + + + + + + + + ) + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } + + renderRestartDialog() { + return ( + + Confirm Restart + + Are you sure you want to restart the device? + + + + + + + ) + } + + onRestart = () => { + this.setState({ confirmRestart: true }); + } + + onRestartRejected = () => { + this.setState({ confirmRestart: false }); + } + + onRestartConfirmed = () => { + this.setState({ processing: true }); + redirectingAuthorizedFetch(RESTART_ENDPOINT, { method: 'POST' }) + .then(response => { + if (response.status === 200) { + this.props.enqueueSnackbar("Device is restarting", { variant: 'info' }); + this.setState({ processing: false, confirmRestart: false }); + } else { + throw Error("Invalid status code: " + response.status); + } + }) + .catch(error => { + this.props.enqueueSnackbar(error.message || "Problem restarting device", { variant: 'error' }); + this.setState({ processing: false, confirmRestart: false }); + }); + } + + renderFactoryResetDialog() { + return ( + + Confirm Factory Reset + + Are you sure you want to reset the device to its factory defaults? + + + + } variant="contained" onClick={this.onFactoryResetConfirmed} disabled={this.state.processing} autoFocus> + Factory Reset + + + + ) + } + + onFactoryReset = () => { + this.setState({ confirmFactoryReset: true }); + } + + onFactoryResetRejected = () => { + this.setState({ confirmFactoryReset: false }); + } + + onFactoryResetConfirmed = () => { + this.setState({ processing: true }); + redirectingAuthorizedFetch(FACTORY_RESET_ENDPOINT, { method: 'POST' }) + .then(response => { + if (response.status === 200) { + this.props.enqueueSnackbar("Factory reset in progress.", { variant: 'error' }); + this.setState({ processing: false, confirmFactoryReset: false }); + } else { + throw Error("Invalid status code: " + response.status); + } + }) + .catch(error => { + this.props.enqueueSnackbar(error.message || "Problem factory resetting device", { variant: 'error' }); + this.setState({ processing: false, confirmRestart: false }); + }); + } + + render() { + const me = this.props.authenticatedContext.me; + return ( + + + {this.createListItems()} + + + + } variant="contained" color="secondary" onClick={this.props.loadData}> + Refresh + + + {me.admin && + + } variant="contained" color="primary" onClick={this.onRestart}> + Restart + + } variant="contained" onClick={this.onFactoryReset}> + Factory reset + + + } + + {this.renderRestartDialog()} + {this.renderFactoryResetDialog()} + + ); + } + +} + +export default withAuthenticatedContext(SystemStatusForm); diff --git a/interface/src/system/UploadFirmwareController.tsx b/interface/src/system/UploadFirmwareController.tsx new file mode 100644 index 0000000..16d21ff --- /dev/null +++ b/interface/src/system/UploadFirmwareController.tsx @@ -0,0 +1,71 @@ +import React, { Component } from 'react'; + +import { SectionContent } from '../components'; +import { UPLOAD_FIRMWARE_ENDPOINT } from '../api'; + +import UploadFirmwareForm from './UploadFirmwareForm'; +import { redirectingAuthorizedUpload } from '../authentication'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; + +interface UploadFirmwareControllerState { + xhr?: XMLHttpRequest; + progress?: ProgressEvent; +} + +class UploadFirmwareController extends Component { + + state: UploadFirmwareControllerState = { + xhr: undefined, + progress: undefined + }; + + componentWillUnmount() { + this.state.xhr?.abort(); + } + + updateProgress = (progress: ProgressEvent) => { + this.setState({ progress }); + } + + uploadFile = (file: File) => { + if (this.state.xhr) { + return; + } + var xhr = new XMLHttpRequest(); + this.setState({ xhr }); + redirectingAuthorizedUpload(xhr, UPLOAD_FIRMWARE_ENDPOINT, file, this.updateProgress).then(() => { + if (xhr.status !== 200) { + throw Error("Invalid status code: " + xhr.status); + } + this.props.enqueueSnackbar("Activating new firmware", { variant: 'success' }); + this.setState({ xhr: undefined, progress: undefined }); + }).catch((error: Error) => { + if (error.name === 'AbortError') { + this.props.enqueueSnackbar("Upload cancelled by user", { variant: 'warning' }); + } else { + const errorMessage = error.name === 'UploadError' ? "Error during upload" : (error.message || "Unknown error"); + this.props.enqueueSnackbar("Problem uploading: " + errorMessage, { variant: 'error' }); + this.setState({ xhr: undefined, progress: undefined }); + } + }); + } + + cancelUpload = () => { + if (this.state.xhr) { + this.state.xhr.abort(); + this.setState({ xhr: undefined, progress: undefined }); + } + } + + render() { + const { xhr, progress } = this.state; + return ( + + + + ); + } + +} + +export default withSnackbar(UploadFirmwareController); diff --git a/interface/src/system/UploadFirmwareForm.tsx b/interface/src/system/UploadFirmwareForm.tsx new file mode 100644 index 0000000..2c40be3 --- /dev/null +++ b/interface/src/system/UploadFirmwareForm.tsx @@ -0,0 +1,35 @@ +import React, { Fragment } from 'react'; +import { SingleUpload } from '../components'; +import { Box } from '@material-ui/core'; + +interface UploadFirmwareFormProps { + uploading: boolean; + progress?: ProgressEvent; + onFileSelected: (file: File) => void; + onCancel: () => void; +} + +class UploadFirmwareForm extends React.Component { + + handleDrop = (files: File[]) => { + const file = files[0]; + if (file) { + this.props.onFileSelected(files[0]); + } + }; + + render() { + const { uploading, progress, onCancel } = this.props; + return ( + + + Upload a new firmware (.bin) file below to replace the existing firmware. + + + + ); + } + +} + +export default UploadFirmwareForm; diff --git a/interface/src/system/types.ts b/interface/src/system/types.ts new file mode 100644 index 0000000..67b15e2 --- /dev/null +++ b/interface/src/system/types.ts @@ -0,0 +1,37 @@ +export enum EspPlatform { + ESP8266 = "esp8266", + ESP32 = "esp32" +} + +interface ESPSystemStatus { + esp_platform: EspPlatform; + max_alloc_heap: number; + cpu_freq_mhz: number; + free_heap: number; + sketch_size: number; + free_sketch_space: number; + sdk_version: string; + flash_chip_size: number; + flash_chip_speed: number; + fs_used: number; + fs_total: number; +} + +export interface ESP32SystemStatus extends ESPSystemStatus { + esp_platform: EspPlatform.ESP32; + psram_size: number; + free_psram: number; +} + +export interface ESP8266SystemStatus extends ESPSystemStatus { + esp_platform: EspPlatform.ESP8266; + heap_fragmentation: number; +} + +export type SystemStatus = ESP8266SystemStatus | ESP32SystemStatus; + +export interface OTASettings { + enabled: boolean; + port: number; + password: string; +} diff --git a/interface/src/validators/index.ts b/interface/src/validators/index.ts new file mode 100644 index 0000000..82aafa8 --- /dev/null +++ b/interface/src/validators/index.ts @@ -0,0 +1,4 @@ +export { default as isHostname } from './isHostname'; +export { default as isIP } from './isIP'; +export { default as optional } from './optional'; +export { default as or } from './or'; diff --git a/interface/src/validators/isHostname.ts b/interface/src/validators/isHostname.ts new file mode 100644 index 0000000..7c1ca25 --- /dev/null +++ b/interface/src/validators/isHostname.ts @@ -0,0 +1,6 @@ +const hostnameLengthRegex = /^.{0,32}$/ +const hostnamePatternRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/ + +export default function isHostname(hostname: string) { + return hostnameLengthRegex.test(hostname) && hostnamePatternRegex.test(hostname); +} diff --git a/interface/src/validators/isIP.ts b/interface/src/validators/isIP.ts new file mode 100644 index 0000000..439c57c --- /dev/null +++ b/interface/src/validators/isIP.ts @@ -0,0 +1,5 @@ +const ipAddressRegexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ + +export default function isIp(ipAddress: string) { + return ipAddressRegexp.test(ipAddress); +} \ No newline at end of file diff --git a/interface/src/validators/optional.ts b/interface/src/validators/optional.ts new file mode 100644 index 0000000..841c57b --- /dev/null +++ b/interface/src/validators/optional.ts @@ -0,0 +1 @@ +export default (validator: (value: any) => boolean) => (value: any) => !value || validator(value); diff --git a/interface/src/validators/or.ts b/interface/src/validators/or.ts new file mode 100644 index 0000000..047ecf0 --- /dev/null +++ b/interface/src/validators/or.ts @@ -0,0 +1,3 @@ +export default (validator1: (value: any) => boolean, validator2: (value: any) => boolean) => { + return (value: any) => validator1(value) || validator2(value); +} diff --git a/interface/src/wifi/WiFiConnection.tsx b/interface/src/wifi/WiFiConnection.tsx new file mode 100644 index 0000000..c6cef06 --- /dev/null +++ b/interface/src/wifi/WiFiConnection.tsx @@ -0,0 +1,62 @@ +import React, { Component } from 'react'; +import { Redirect, Switch, RouteComponentProps } from 'react-router-dom' + +import { Tabs, Tab } from '@material-ui/core'; + +import { withAuthenticatedContext, AuthenticatedContextProps, AuthenticatedRoute } from '../authentication'; +import { MenuAppBar } from '../components'; + +import WiFiStatusController from './WiFiStatusController'; +import WiFiSettingsController from './WiFiSettingsController'; +import WiFiNetworkScanner from './WiFiNetworkScanner'; +import { WiFiConnectionContext } from './WiFiConnectionContext'; +import { WiFiNetwork } from './types'; + +type WiFiConnectionProps = AuthenticatedContextProps & RouteComponentProps; + +class WiFiConnection extends Component { + + constructor(props: WiFiConnectionProps) { + super(props); + this.state = { + selectNetwork: this.selectNetwork, + deselectNetwork: this.deselectNetwork + }; + } + + selectNetwork = (network: WiFiNetwork) => { + this.setState({ selectedNetwork: network }); + this.props.history.push('/wifi/settings'); + } + + deselectNetwork = () => { + this.setState({ selectedNetwork: undefined }); + } + + handleTabChange = (event: React.ChangeEvent<{}>, path: string) => { + this.props.history.push(path); + }; + + render() { + const { authenticatedContext } = this.props; + return ( + + + + + + + + + + + + + + + + ) + } +} + +export default withAuthenticatedContext(WiFiConnection); diff --git a/interface/src/wifi/WiFiConnectionContext.tsx b/interface/src/wifi/WiFiConnectionContext.tsx new file mode 100644 index 0000000..85b0c17 --- /dev/null +++ b/interface/src/wifi/WiFiConnectionContext.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { WiFiNetwork } from './types'; + +export interface WiFiConnectionContext { + selectedNetwork?: WiFiNetwork; + selectNetwork: (network: WiFiNetwork) => void; + deselectNetwork: () => void; +} + +const WiFiConnectionContextDefaultValue = {} as WiFiConnectionContext +export const WiFiConnectionContext = React.createContext( + WiFiConnectionContextDefaultValue +); diff --git a/interface/src/wifi/WiFiNetworkScanner.tsx b/interface/src/wifi/WiFiNetworkScanner.tsx new file mode 100644 index 0000000..744f515 --- /dev/null +++ b/interface/src/wifi/WiFiNetworkScanner.tsx @@ -0,0 +1,168 @@ +import React, { Component } from 'react'; +import { withSnackbar, WithSnackbarProps } from 'notistack'; + +import { createStyles, WithStyles, Theme, withStyles, Typography, LinearProgress } from '@material-ui/core'; +import PermScanWifiIcon from '@material-ui/icons/PermScanWifi'; + +import { FormActions, FormButton, SectionContent } from '../components'; +import { redirectingAuthorizedFetch } from '../authentication'; +import { SCAN_NETWORKS_ENDPOINT, LIST_NETWORKS_ENDPOINT } from '../api'; + +import WiFiNetworkSelector from './WiFiNetworkSelector'; +import { WiFiNetworkList, WiFiNetwork } from './types'; + +const NUM_POLLS = 10 +const POLLING_FREQUENCY = 500 +const RETRY_EXCEPTION_TYPE = "retry" + +interface WiFiNetworkScannerState { + scanningForNetworks: boolean; + errorMessage?: string; + networkList?: WiFiNetworkList; +} + +const styles = (theme: Theme) => createStyles({ + scanningSettings: { + margin: theme.spacing(0.5), + }, + scanningSettingsDetails: { + margin: theme.spacing(4), + textAlign: "center" + }, + scanningProgress: { + margin: theme.spacing(4), + textAlign: "center" + } +}); + +type WiFiNetworkScannerProps = WithSnackbarProps & WithStyles; + +class WiFiNetworkScanner extends Component { + + pollCount: number = 0; + + state: WiFiNetworkScannerState = { + scanningForNetworks: false, + }; + + componentDidMount() { + this.scanNetworks(); + } + + requestNetworkScan = () => { + const { scanningForNetworks } = this.state; + if (!scanningForNetworks) { + this.scanNetworks(); + } + } + + scanNetworks() { + this.pollCount = 0; + this.setState({ scanningForNetworks: true, networkList: undefined, errorMessage: undefined }); + redirectingAuthorizedFetch(SCAN_NETWORKS_ENDPOINT).then(response => { + if (response.status === 202) { + this.schedulePollTimeout(); + return; + } + throw Error("Scanning for networks returned unexpected response code: " + response.status); + }).catch(error => { + this.props.enqueueSnackbar("Problem scanning: " + error.message, { + variant: 'error', + }); + this.setState({ scanningForNetworks: false, networkList: undefined, errorMessage: error.message }); + }); + } + + schedulePollTimeout() { + setTimeout(this.pollNetworkList, POLLING_FREQUENCY); + } + + retryError() { + return { + name: RETRY_EXCEPTION_TYPE, + message: "Network list not ready, will retry in " + POLLING_FREQUENCY + "ms." + }; + } + + compareNetworks(network1: WiFiNetwork, network2: WiFiNetwork) { + if (network1.rssi < network2.rssi) + return 1; + if (network1.rssi > network2.rssi) + return -1; + return 0; + } + + pollNetworkList = () => { + redirectingAuthorizedFetch(LIST_NETWORKS_ENDPOINT) + .then(response => { + if (response.status === 200) { + return response.json(); + } + if (response.status === 202) { + if (++this.pollCount < NUM_POLLS) { + this.schedulePollTimeout(); + throw this.retryError(); + } else { + throw Error("Device did not return network list in timely manner."); + } + } + throw Error("Device returned unexpected response code: " + response.status); + }) + .then(json => { + json.networks.sort(this.compareNetworks) + this.setState({ scanningForNetworks: false, networkList: json, errorMessage: undefined }) + }) + .catch(error => { + if (error.name !== RETRY_EXCEPTION_TYPE) { + this.props.enqueueSnackbar("Problem scanning: " + error.message, { + variant: 'error', + }); + this.setState({ scanningForNetworks: false, networkList: undefined, errorMessage: error.message }); + } + }); + } + + renderNetworkScanner() { + const { classes } = this.props; + const { scanningForNetworks, networkList, errorMessage } = this.state; + if (scanningForNetworks || !networkList) { + return ( +
+ + + Scanning… + +
+ ); + } + if (errorMessage) { + return ( +
+ + {errorMessage} + +
+ ); + } + return ( + + ); + } + + render() { + const { scanningForNetworks } = this.state; + return ( + + {this.renderNetworkScanner()} + + } variant="contained" color="secondary" onClick={this.requestNetworkScan} disabled={scanningForNetworks}> + Scan again… + + + + ); + } + +} + +export default withSnackbar(withStyles(styles)(WiFiNetworkScanner)); diff --git a/interface/src/wifi/WiFiNetworkSelector.tsx b/interface/src/wifi/WiFiNetworkSelector.tsx new file mode 100644 index 0000000..2043651 --- /dev/null +++ b/interface/src/wifi/WiFiNetworkSelector.tsx @@ -0,0 +1,54 @@ +import React, { Component } from 'react'; + +import { Avatar, Badge } from '@material-ui/core'; +import { List, ListItem, ListItemIcon, ListItemText, ListItemAvatar } from '@material-ui/core'; + +import WifiIcon from '@material-ui/icons/Wifi'; +import LockIcon from '@material-ui/icons/Lock'; +import LockOpenIcon from '@material-ui/icons/LockOpen'; + +import { isNetworkOpen, networkSecurityMode } from './WiFiSecurityModes'; +import { WiFiConnectionContext } from './WiFiConnectionContext'; +import { WiFiNetwork, WiFiNetworkList } from './types'; + +interface WiFiNetworkSelectorProps { + networkList: WiFiNetworkList; +} + +class WiFiNetworkSelector extends Component { + + static contextType = WiFiConnectionContext; + context!: React.ContextType; + + renderNetwork = (network: WiFiNetwork) => { + return ( + this.context.selectNetwork(network)}> + + + {isNetworkOpen(network) ? : } + + + + + + + + + + ); + } + + render() { + return ( + + {this.props.networkList.networks.map(this.renderNetwork)} + + ); + } + +} + +export default WiFiNetworkSelector; diff --git a/interface/src/wifi/WiFiSecurityModes.ts b/interface/src/wifi/WiFiSecurityModes.ts new file mode 100644 index 0000000..b65c69a --- /dev/null +++ b/interface/src/wifi/WiFiSecurityModes.ts @@ -0,0 +1,21 @@ +import { WiFiNetwork, WiFiEncryptionType } from "./types"; + +export const isNetworkOpen = ({ encryption_type }: WiFiNetwork) => encryption_type === WiFiEncryptionType.WIFI_AUTH_OPEN; + +export const networkSecurityMode = ({ encryption_type }: WiFiNetwork) => { + switch (encryption_type) { + case WiFiEncryptionType.WIFI_AUTH_WEP: + case WiFiEncryptionType.WIFI_AUTH_WEP_PSK: + return "WEP"; + case WiFiEncryptionType.WIFI_AUTH_WEP2_PSK: + return "WEP2"; + case WiFiEncryptionType.WIFI_AUTH_WPA_WPA2_PSK: + return "WPA/WEP2"; + case WiFiEncryptionType.WIFI_AUTH_WPA2_ENTERPRISE: + return "WEP2 Enterprise"; + case WiFiEncryptionType.WIFI_AUTH_OPEN: + return "None"; + default: + return "Unknown"; + } +} diff --git a/interface/src/wifi/WiFiSettingsController.tsx b/interface/src/wifi/WiFiSettingsController.tsx new file mode 100644 index 0000000..d0613f8 --- /dev/null +++ b/interface/src/wifi/WiFiSettingsController.tsx @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; + +import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import WiFiSettingsForm from './WiFiSettingsForm'; +import { WIFI_SETTINGS_ENDPOINT } from '../api'; +import { WiFiSettings } from './types'; + +type WiFiSettingsControllerProps = RestControllerProps; + +class WiFiSettingsController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(WIFI_SETTINGS_ENDPOINT, WiFiSettingsController); diff --git a/interface/src/wifi/WiFiSettingsForm.tsx b/interface/src/wifi/WiFiSettingsForm.tsx new file mode 100644 index 0000000..04aea28 --- /dev/null +++ b/interface/src/wifi/WiFiSettingsForm.tsx @@ -0,0 +1,200 @@ +import React, { Fragment } from 'react'; +import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; + +import { Checkbox, List, ListItem, ListItemText, ListItemAvatar, ListItemSecondaryAction } from '@material-ui/core'; + +import Avatar from '@material-ui/core/Avatar'; +import IconButton from '@material-ui/core/IconButton'; +import LockIcon from '@material-ui/icons/Lock'; +import LockOpenIcon from '@material-ui/icons/LockOpen'; +import DeleteIcon from '@material-ui/icons/Delete'; +import SaveIcon from '@material-ui/icons/Save'; + +import { RestFormProps, PasswordValidator, BlockFormControlLabel, FormActions, FormButton } from '../components'; +import { isIP, isHostname, optional } from '../validators'; + +import { WiFiConnectionContext } from './WiFiConnectionContext'; +import { isNetworkOpen, networkSecurityMode } from './WiFiSecurityModes'; +import { WiFiSettings } from './types'; + +type WiFiStatusFormProps = RestFormProps; + +class WiFiSettingsForm extends React.Component { + + static contextType = WiFiConnectionContext; + context!: React.ContextType; + + constructor(props: WiFiStatusFormProps, context: WiFiConnectionContext) { + super(props); + + const { selectedNetwork } = context; + if (selectedNetwork) { + const wifiSettings: WiFiSettings = { + ssid: selectedNetwork.ssid, + password: "", + hostname: props.data.hostname, + static_ip_config: false, + } + props.setData(wifiSettings); + } + } + + componentWillMount() { + ValidatorForm.addValidationRule('isIP', isIP); + ValidatorForm.addValidationRule('isHostname', isHostname); + ValidatorForm.addValidationRule('isOptionalIP', optional(isIP)); + } + + deselectNetworkAndLoadData = () => { + this.context.deselectNetwork(); + this.props.loadData(); + } + + componentWillUnmount() { + this.context.deselectNetwork(); + } + + render() { + const { selectedNetwork, deselectNetwork } = this.context; + const { data, handleValueChange, saveData } = this.props; + return ( + + { + selectedNetwork ? + + + + + {isNetworkOpen(selectedNetwork) ? : } + + + + + + + + + + + : + + } + { + (!selectedNetwork || !isNetworkOpen(selectedNetwork)) && + + } + + + } + label="Static IP Config?" + /> + { + data.static_ip_config && + + + + + + + + } + + } variant="contained" color="primary" type="submit"> + Save + + + + ); + } +} + +export default WiFiSettingsForm; diff --git a/interface/src/wifi/WiFiStatus.ts b/interface/src/wifi/WiFiStatus.ts new file mode 100644 index 0000000..2d3574f --- /dev/null +++ b/interface/src/wifi/WiFiStatus.ts @@ -0,0 +1,41 @@ +import { Theme } from '@material-ui/core'; +import { WiFiStatus, WiFiConnectionStatus } from './types'; + +export const isConnected = ({ status }: WiFiStatus) => status === WiFiConnectionStatus.WIFI_STATUS_CONNECTED; + +export const wifiStatusHighlight = ({ status }: WiFiStatus, theme: Theme) => { + switch (status) { + case WiFiConnectionStatus.WIFI_STATUS_IDLE: + case WiFiConnectionStatus.WIFI_STATUS_DISCONNECTED: + case WiFiConnectionStatus.WIFI_STATUS_NO_SHIELD: + return theme.palette.info.main; + case WiFiConnectionStatus.WIFI_STATUS_CONNECTED: + return theme.palette.success.main; + case WiFiConnectionStatus.WIFI_STATUS_CONNECT_FAILED: + case WiFiConnectionStatus.WIFI_STATUS_CONNECTION_LOST: + return theme.palette.error.main; + default: + return theme.palette.warning.main; + } +} + +export const wifiStatus = ({ status }: WiFiStatus) => { + switch (status) { + case WiFiConnectionStatus.WIFI_STATUS_NO_SHIELD: + return "Inactive"; + case WiFiConnectionStatus.WIFI_STATUS_IDLE: + return "Idle"; + case WiFiConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL: + return "No SSID Available"; + case WiFiConnectionStatus.WIFI_STATUS_CONNECTED: + return "Connected"; + case WiFiConnectionStatus.WIFI_STATUS_CONNECT_FAILED: + return "Connection Failed"; + case WiFiConnectionStatus.WIFI_STATUS_CONNECTION_LOST: + return "Connection Lost"; + case WiFiConnectionStatus.WIFI_STATUS_DISCONNECTED: + return "Disconnected"; + default: + return "Unknown"; + } +} diff --git a/interface/src/wifi/WiFiStatusController.tsx b/interface/src/wifi/WiFiStatusController.tsx new file mode 100644 index 0000000..2220595 --- /dev/null +++ b/interface/src/wifi/WiFiStatusController.tsx @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; + +import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import WiFiStatusForm from './WiFiStatusForm'; +import { WIFI_STATUS_ENDPOINT } from '../api'; +import { WiFiStatus } from './types'; + +type WiFiStatusControllerProps = RestControllerProps; + +class WiFiStatusController extends Component { + + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } + +} + +export default restController(WIFI_STATUS_ENDPOINT, WiFiStatusController); diff --git a/interface/src/wifi/WiFiStatusForm.tsx b/interface/src/wifi/WiFiStatusForm.tsx new file mode 100644 index 0000000..7d5307c --- /dev/null +++ b/interface/src/wifi/WiFiStatusForm.tsx @@ -0,0 +1,117 @@ +import React, { Component, Fragment } from 'react'; + +import { WithTheme, withTheme } from '@material-ui/core/styles'; +import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; + +import DNSIcon from '@material-ui/icons/Dns'; +import WifiIcon from '@material-ui/icons/Wifi'; +import SettingsInputComponentIcon from '@material-ui/icons/SettingsInputComponent'; +import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; +import DeviceHubIcon from '@material-ui/icons/DeviceHub'; +import RefreshIcon from '@material-ui/icons/Refresh'; + +import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components'; +import { wifiStatus, wifiStatusHighlight, isConnected } from './WiFiStatus'; +import { WiFiStatus } from './types'; + +type WiFiStatusFormProps = RestFormProps & WithTheme; + +class WiFiStatusForm extends Component { + + dnsServers(status: WiFiStatus) { + if (!status.dns_ip_1) { + return "none"; + } + return status.dns_ip_1 + (status.dns_ip_2 ? ',' + status.dns_ip_2 : ''); + } + + createListItems() { + const { data, theme } = this.props + return ( + + + + + + + + + + + { + isConnected(data) && + + + + + + + + + + + + + IP + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + } + + ); + } + + render() { + return ( + + + {this.createListItems()} + + + } variant="contained" color="secondary" onClick={this.props.loadData}> + Refresh + + + + ); + } + +} + +export default withTheme(WiFiStatusForm); diff --git a/interface/src/wifi/types.ts b/interface/src/wifi/types.ts new file mode 100644 index 0000000..d631051 --- /dev/null +++ b/interface/src/wifi/types.ts @@ -0,0 +1,56 @@ +export enum WiFiConnectionStatus { + WIFI_STATUS_IDLE = 0, + WIFI_STATUS_NO_SSID_AVAIL = 1, + WIFI_STATUS_CONNECTED = 3, + WIFI_STATUS_CONNECT_FAILED = 4, + WIFI_STATUS_CONNECTION_LOST = 5, + WIFI_STATUS_DISCONNECTED = 6, + WIFI_STATUS_NO_SHIELD = 255 +} + +export enum WiFiEncryptionType { + WIFI_AUTH_OPEN = 0, + WIFI_AUTH_WEP = 1, + WIFI_AUTH_WEP_PSK = 2, + WIFI_AUTH_WEP2_PSK = 3, + WIFI_AUTH_WPA_WPA2_PSK = 4, + WIFI_AUTH_WPA2_ENTERPRISE = 5 +} + +export interface WiFiStatus { + status: WiFiConnectionStatus; + local_ip: string; + mac_address: string; + rssi: number; + ssid: string; + bssid: string; + channel: number; + subnet_mask: string; + gateway_ip: string; + dns_ip_1: string; + dns_ip_2: string; +} + +export interface WiFiSettings { + ssid: string; + password: string; + hostname: string; + static_ip_config: boolean; + local_ip?: string; + gateway_ip?: string; + subnet_mask?: string; + dns_ip_1?: string; + dns_ip_2?: string; +} + +export interface WiFiNetworkList { + networks: WiFiNetwork[]; +} + +export interface WiFiNetwork { + rssi: number; + ssid: string; + bssid: string; + channel: number; + encryption_type: WiFiEncryptionType; +} diff --git a/interface/tsconfig.json b/interface/tsconfig.json new file mode 100644 index 0000000..f2850b7 --- /dev/null +++ b/interface/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + }, + "include": [ + "src" + ] +} diff --git a/lib/framework/APSettingsService.cpp b/lib/framework/APSettingsService.cpp new file mode 100644 index 0000000..0bfcbb4 --- /dev/null +++ b/lib/framework/APSettingsService.cpp @@ -0,0 +1,83 @@ +#include + +APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE), + _dnsServer(nullptr), + _lastManaged(0), + _reconfigureAp(false) { + addUpdateHandler([&](const String& originId) { reconfigureAP(); }, false); +} + +void APSettingsService::begin() { + _fsPersistence.readFromFS(); + reconfigureAP(); +} + +void APSettingsService::reconfigureAP() { + _lastManaged = millis() - MANAGE_NETWORK_DELAY; + _reconfigureAp = true; +} + +void APSettingsService::loop() { + unsigned long currentMillis = millis(); + unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); + if (manageElapsed >= MANAGE_NETWORK_DELAY) { + _lastManaged = currentMillis; + manageAP(); + } + handleDNS(); +} + +void APSettingsService::manageAP() { + WiFiMode_t currentWiFiMode = WiFi.getMode(); + if (_state.provisionMode == AP_MODE_ALWAYS || + (_state.provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED)) { + if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) { + startAP(); + } + } else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && + (_reconfigureAp || !WiFi.softAPgetStationNum())) { + stopAP(); + } + _reconfigureAp = false; +} + +void APSettingsService::startAP() { + Serial.println(F("Starting software access point")); + WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask); + WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients); + if (!_dnsServer) { + IPAddress apIp = WiFi.softAPIP(); + Serial.print(F("Starting captive portal on ")); + Serial.println(apIp); + _dnsServer = new DNSServer; + _dnsServer->start(DNS_PORT, "*", apIp); + } +} + +void APSettingsService::stopAP() { + if (_dnsServer) { + Serial.println(F("Stopping captive portal")); + _dnsServer->stop(); + delete _dnsServer; + _dnsServer = nullptr; + } + Serial.println(F("Stopping software access point")); + WiFi.softAPdisconnect(true); +} + +void APSettingsService::handleDNS() { + if (_dnsServer) { + _dnsServer->processNextRequest(); + } +} + +APNetworkStatus APSettingsService::getAPNetworkStatus() { + WiFiMode_t currentWiFiMode = WiFi.getMode(); + bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA; + if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) { + return APNetworkStatus::LINGERING; + } + return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE; +} diff --git a/lib/framework/APSettingsService.h b/lib/framework/APSettingsService.h new file mode 100644 index 0000000..998f290 --- /dev/null +++ b/lib/framework/APSettingsService.h @@ -0,0 +1,146 @@ +#ifndef APSettingsConfig_h +#define APSettingsConfig_h + +#include +#include +#include +#include + +#include +#include + +#ifndef FACTORY_AP_PROVISION_MODE +#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED +#endif + +#ifndef FACTORY_AP_SSID +#define FACTORY_AP_SSID "ESP8266-React-#{unique_id}" +#endif + +#ifndef FACTORY_AP_PASSWORD +#define FACTORY_AP_PASSWORD "esp-react" +#endif + +#ifndef FACTORY_AP_LOCAL_IP +#define FACTORY_AP_LOCAL_IP "192.168.4.1" +#endif + +#ifndef FACTORY_AP_GATEWAY_IP +#define FACTORY_AP_GATEWAY_IP "192.168.4.1" +#endif + +#ifndef FACTORY_AP_SUBNET_MASK +#define FACTORY_AP_SUBNET_MASK "255.255.255.0" +#endif + +#ifndef FACTORY_AP_CHANNEL +#define FACTORY_AP_CHANNEL 1 +#endif + +#ifndef FACTORY_AP_SSID_HIDDEN +#define FACTORY_AP_SSID_HIDDEN false +#endif + +#ifndef FACTORY_AP_MAX_CLIENTS +#define FACTORY_AP_MAX_CLIENTS 4 +#endif + +#define AP_SETTINGS_FILE "/config/apSettings.json" +#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings" + +#define AP_MODE_ALWAYS 0 +#define AP_MODE_DISCONNECTED 1 +#define AP_MODE_NEVER 2 + +#define MANAGE_NETWORK_DELAY 10000 +#define DNS_PORT 53 + +enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING }; + +class APSettings { + public: + uint8_t provisionMode; + String ssid; + String password; + uint8_t channel; + bool ssidHidden; + uint8_t maxClients; + + IPAddress localIP; + IPAddress gatewayIP; + IPAddress subnetMask; + + bool operator==(const APSettings& settings) const { + return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password && + channel == settings.channel && ssidHidden == settings.ssidHidden && maxClients == settings.maxClients && + localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask; + } + + static void read(APSettings& settings, JsonObject& root) { + root["provision_mode"] = settings.provisionMode; + root["ssid"] = settings.ssid; + root["password"] = settings.password; + root["channel"] = settings.channel; + root["ssid_hidden"] = settings.ssidHidden; + root["max_clients"] = settings.maxClients; + root["local_ip"] = settings.localIP.toString(); + root["gateway_ip"] = settings.gatewayIP.toString(); + root["subnet_mask"] = settings.subnetMask.toString(); + } + + static StateUpdateResult update(JsonObject& root, APSettings& settings) { + APSettings newSettings = {}; + newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE; + switch (settings.provisionMode) { + case AP_MODE_ALWAYS: + case AP_MODE_DISCONNECTED: + case AP_MODE_NEVER: + break; + default: + newSettings.provisionMode = AP_MODE_ALWAYS; + } + newSettings.ssid = root["ssid"] | SettingValue::format(FACTORY_AP_SSID); + newSettings.password = root["password"] | FACTORY_AP_PASSWORD; + newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL; + newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN; + newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS; + + JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP); + JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP); + JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK); + + if (newSettings == settings) { + return StateUpdateResult::UNCHANGED; + } + settings = newSettings; + return StateUpdateResult::CHANGED; + } +}; + +class APSettingsService : public StatefulService { + public: + APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + + void begin(); + void loop(); + APNetworkStatus getAPNetworkStatus(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + + // for the captive portal + DNSServer* _dnsServer; + + // for the mangement delay loop + volatile unsigned long _lastManaged; + volatile boolean _reconfigureAp; + + void reconfigureAP(); + void manageAP(); + void startAP(); + void stopAP(); + void handleDNS(); +}; + +#endif // end APSettingsConfig_h diff --git a/lib/framework/APStatus.cpp b/lib/framework/APStatus.cpp new file mode 100644 index 0000000..5bfe300 --- /dev/null +++ b/lib/framework/APStatus.cpp @@ -0,0 +1,22 @@ +#include + +APStatus::APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService) : + _apSettingsService(apSettingsService) { + server->on(AP_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); +} + +void APStatus::apStatus(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE); + JsonObject root = response->getRoot(); + + root["status"] = _apSettingsService->getAPNetworkStatus(); + root["ip_address"] = WiFi.softAPIP().toString(); + root["mac_address"] = WiFi.softAPmacAddress(); + root["station_num"] = WiFi.softAPgetStationNum(); + + response->setLength(); + request->send(response); +} diff --git a/lib/framework/APStatus.h b/lib/framework/APStatus.h new file mode 100644 index 0000000..12620b0 --- /dev/null +++ b/lib/framework/APStatus.h @@ -0,0 +1,31 @@ +#ifndef APStatus_h +#define APStatus_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#define MAX_AP_STATUS_SIZE 1024 +#define AP_STATUS_SERVICE_PATH "/rest/apStatus" + +class APStatus { + public: + APStatus(AsyncWebServer* server, SecurityManager* securityManager, APSettingsService* apSettingsService); + + private: + APSettingsService* _apSettingsService; + void apStatus(AsyncWebServerRequest* request); +}; + +#endif // end APStatus_h diff --git a/lib/framework/ArduinoJsonJWT.cpp b/lib/framework/ArduinoJsonJWT.cpp new file mode 100644 index 0000000..8b449e1 --- /dev/null +++ b/lib/framework/ArduinoJsonJWT.cpp @@ -0,0 +1,144 @@ +#include "ArduinoJsonJWT.h" + +ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { +} + +void ArduinoJsonJWT::setSecret(String secret) { + _secret = secret; +} + +String ArduinoJsonJWT::getSecret() { + return _secret; +} + +/* + * ESP32 uses mbedtls, ESP2866 uses bearssl. + * + * Both come with decent HMAC implmentations supporting sha256, as well as others. + * + * No need to pull in additional crypto libraries - lets use what we already have. + */ +String ArduinoJsonJWT::sign(String& payload) { + unsigned char hmacResult[32]; + { +#ifdef ESP32 + mbedtls_md_context_t ctx; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); + mbedtls_md_hmac_starts(&ctx, (unsigned char*)_secret.c_str(), _secret.length()); + mbedtls_md_hmac_update(&ctx, (unsigned char*)payload.c_str(), payload.length()); + mbedtls_md_hmac_finish(&ctx, hmacResult); + mbedtls_md_free(&ctx); +#elif defined(ESP8266) + br_hmac_key_context keyCtx; + br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length()); + br_hmac_context hmacCtx; + br_hmac_init(&hmacCtx, &keyCtx, 0); + br_hmac_update(&hmacCtx, payload.c_str(), payload.length()); + br_hmac_out(&hmacCtx, hmacResult); +#endif + } + return encode((char*)hmacResult, 32); +} + +String ArduinoJsonJWT::buildJWT(JsonObject& payload) { + // serialize, then encode payload + String jwt; + serializeJson(payload, jwt); + jwt = encode(jwt.c_str(), jwt.length()); + + // add the header to payload + jwt = JWT_HEADER + '.' + jwt; + + // add signature + jwt += '.' + sign(jwt); + + return jwt; +} + +void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument& jsonDocument) { + // clear json document before we begin, jsonDocument wil be null on failure + jsonDocument.clear(); + + // must have the correct header and delimiter + if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) { + return; + } + + // check there is a signature delimieter + int signatureDelimiterIndex = jwt.lastIndexOf('.'); + if (signatureDelimiterIndex == JWT_HEADER_SIZE) { + return; + } + + // check the signature is valid + String signature = jwt.substring(signatureDelimiterIndex + 1); + jwt = jwt.substring(0, signatureDelimiterIndex); + if (sign(jwt) != signature) { + return; + } + + // decode payload + jwt = jwt.substring(JWT_HEADER_SIZE + 1); + jwt = decode(jwt); + + // parse payload, clearing json document after failure + DeserializationError error = deserializeJson(jsonDocument, jwt); + if (error != DeserializationError::Ok || !jsonDocument.is()) { + jsonDocument.clear(); + } +} + +String ArduinoJsonJWT::encode(const char* cstr, int inputLen) { + // prepare encoder + base64_encodestate _state; +#ifdef ESP32 + base64_init_encodestate(&_state); + size_t encodedLength = base64_encode_expected_len(inputLen) + 1; +#elif defined(ESP8266) + base64_init_encodestate_nonewlines(&_state); + size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1; +#endif + // prepare buffer of correct length, returning an empty string on failure + char* buffer = (char*)malloc(encodedLength * sizeof(char)); + if (buffer == nullptr) { + return ""; + } + + // encode to buffer + int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state); + len += base64_encode_blockend(&buffer[len], &_state); + buffer[len] = 0; + + // convert to arduino string, freeing buffer + String value = String(buffer); + free(buffer); + buffer = nullptr; + + // remove padding and convert to URL safe form + while (value.length() > 0 && value.charAt(value.length() - 1) == '=') { + value.remove(value.length() - 1); + } + value.replace('+', '-'); + value.replace('/', '_'); + + // return as string + return value; +} + +String ArduinoJsonJWT::decode(String value) { + // convert to standard base64 + value.replace('-', '+'); + value.replace('_', '/'); + + // prepare buffer of correct length + char buffer[base64_decode_expected_len(value.length()) + 1]; + + // decode + int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]); + buffer[len] = 0; + + // return as string + return String(buffer); +} diff --git a/lib/framework/ArduinoJsonJWT.h b/lib/framework/ArduinoJsonJWT.h new file mode 100644 index 0000000..beeedc0 --- /dev/null +++ b/lib/framework/ArduinoJsonJWT.h @@ -0,0 +1,37 @@ +#ifndef ArduinoJsonJWT_H +#define ArduinoJsonJWT_H + +#include +#include +#include +#include + +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif + +class ArduinoJsonJWT { + private: + String _secret; + + const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; + const int JWT_HEADER_SIZE = JWT_HEADER.length(); + + String sign(String& value); + + static String encode(const char* cstr, int len); + static String decode(String value); + + public: + ArduinoJsonJWT(String secret); + + void setSecret(String secret); + String getSecret(); + + String buildJWT(JsonObject& payload); + void parseJWT(String jwt, JsonDocument& jsonDocument); +}; + +#endif diff --git a/lib/framework/AuthenticationService.cpp b/lib/framework/AuthenticationService.cpp new file mode 100644 index 0000000..84c347c --- /dev/null +++ b/lib/framework/AuthenticationService.cpp @@ -0,0 +1,48 @@ +#include + +#if FT_ENABLED(FT_SECURITY) + +AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) : + _securityManager(securityManager), + _signInHandler(SIGN_IN_PATH, + std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)) { + server->on(VERIFY_AUTHORIZATION_PATH, + HTTP_GET, + std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1)); + _signInHandler.setMethod(HTTP_POST); + _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); + server->addHandler(&_signInHandler); +} + +/** + * Verifys that the request supplied a valid JWT. + */ +void AuthenticationService::verifyAuthorization(AsyncWebServerRequest* request) { + Authentication authentication = _securityManager->authenticateRequest(request); + request->send(authentication.authenticated ? 200 : 401); +} + +/** + * Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in + * subsequent requests. + */ +void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonVariant& json) { + if (json.is()) { + String username = json["username"]; + String password = json["password"]; + Authentication authentication = _securityManager->authenticate(username, password); + if (authentication.authenticated) { + User* user = authentication.user; + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE); + JsonObject jsonObject = response->getRoot(); + jsonObject["access_token"] = _securityManager->generateJWT(user); + response->setLength(); + request->send(response); + return; + } + } + AsyncWebServerResponse* response = request->beginResponse(401); + request->send(response); +} + +#endif // end FT_ENABLED(FT_SECURITY) diff --git a/lib/framework/AuthenticationService.h b/lib/framework/AuthenticationService.h new file mode 100644 index 0000000..8520223 --- /dev/null +++ b/lib/framework/AuthenticationService.h @@ -0,0 +1,30 @@ +#ifndef AuthenticationService_H_ +#define AuthenticationService_H_ + +#include +#include +#include +#include + +#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization" +#define SIGN_IN_PATH "/rest/signIn" + +#define MAX_AUTHENTICATION_SIZE 256 + +#if FT_ENABLED(FT_SECURITY) + +class AuthenticationService { + public: + AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager); + + private: + SecurityManager* _securityManager; + AsyncCallbackJsonWebHandler _signInHandler; + + // endpoint functions + void signIn(AsyncWebServerRequest* request, JsonVariant& json); + void verifyAuthorization(AsyncWebServerRequest* request); +}; + +#endif // end FT_ENABLED(FT_SECURITY) +#endif // end SecurityManager_h diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp new file mode 100644 index 0000000..5baed6d --- /dev/null +++ b/lib/framework/ESP8266React.cpp @@ -0,0 +1,114 @@ +#include + +ESP8266React::ESP8266React(AsyncWebServer* server) : + _featureService(server), + _securitySettingsService(server, &ESPFS), + _wifiSettingsService(server, &ESPFS, &_securitySettingsService), + _wifiScanner(server, &_securitySettingsService), + _wifiStatus(server, &_securitySettingsService), + _apSettingsService(server, &ESPFS, &_securitySettingsService), + _apStatus(server, &_securitySettingsService, &_apSettingsService), +#if FT_ENABLED(FT_NTP) + _ntpSettingsService(server, &ESPFS, &_securitySettingsService), + _ntpStatus(server, &_securitySettingsService), +#endif +#if FT_ENABLED(FT_OTA) + _otaSettingsService(server, &ESPFS, &_securitySettingsService), +#endif +#if FT_ENABLED(FT_UPLOAD_FIRMWARE) + _uploadFirmwareService(server, &_securitySettingsService), +#endif +#if FT_ENABLED(FT_MQTT) + _mqttSettingsService(server, &ESPFS, &_securitySettingsService), + _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService), +#endif +#if FT_ENABLED(FT_SECURITY) + _authenticationService(server, &_securitySettingsService), +#endif + _restartService(server, &_securitySettingsService), + _factoryResetService(server, &ESPFS, &_securitySettingsService), + _systemStatus(server, &_securitySettingsService) { +#ifdef PROGMEM_WWW + // Serve static resources from PROGMEM + WWWData::registerRoutes( + [server, this](const String& uri, const String& contentType, const uint8_t* content, size_t len) { + ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest* request) { + AsyncWebServerResponse* response = request->beginResponse_P(200, contentType, content, len); + response->addHeader("Content-Encoding", "gzip"); + request->send(response); + }; + server->on(uri.c_str(), HTTP_GET, requestHandler); + // Serving non matching get requests with "/index.html" + // OPTIONS get a straight up 200 response + if (uri.equals("/index.html")) { + server->onNotFound([requestHandler](AsyncWebServerRequest* request) { + if (request->method() == HTTP_GET) { + requestHandler(request); + } else if (request->method() == HTTP_OPTIONS) { + request->send(200); + } else { + request->send(404); + } + }); + } + }); +#else + // Serve static resources from /www/ + server->serveStatic("/js/", ESPFS, "/www/js/"); + server->serveStatic("/css/", ESPFS, "/www/css/"); + server->serveStatic("/fonts/", ESPFS, "/www/fonts/"); + server->serveStatic("/app/", ESPFS, "/www/app/"); + server->serveStatic("/favicon.ico", ESPFS, "/www/favicon.ico"); + // Serving all other get requests with "/www/index.htm" + // OPTIONS get a straight up 200 response + server->onNotFound([](AsyncWebServerRequest* request) { + if (request->method() == HTTP_GET) { + request->send(ESPFS, "/www/index.html"); + } else if (request->method() == HTTP_OPTIONS) { + request->send(200); + } else { + request->send(404); + } + }); +#endif + +// Disable CORS if required +#if defined(ENABLE_CORS) + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); +#endif +} + +void ESP8266React::begin() { +#ifdef ESP32 + ESPFS.begin(true); +#elif defined(ESP8266) + ESPFS.begin(); +#endif + _wifiSettingsService.begin(); + _apSettingsService.begin(); +#if FT_ENABLED(FT_NTP) + _ntpSettingsService.begin(); +#endif +#if FT_ENABLED(FT_OTA) + _otaSettingsService.begin(); +#endif +#if FT_ENABLED(FT_MQTT) + _mqttSettingsService.begin(); +#endif +#if FT_ENABLED(FT_SECURITY) + _securitySettingsService.begin(); +#endif +} + +void ESP8266React::loop() { + _wifiSettingsService.loop(); + _apSettingsService.loop(); +#if FT_ENABLED(FT_OTA) + _otaSettingsService.loop(); +#endif +#if FT_ENABLED(FT_MQTT) + _mqttSettingsService.loop(); +#endif +} diff --git a/lib/framework/ESP8266React.h b/lib/framework/ESP8266React.h new file mode 100644 index 0000000..b1daf8a --- /dev/null +++ b/lib/framework/ESP8266React.h @@ -0,0 +1,122 @@ +#ifndef ESP8266React_h +#define ESP8266React_h + +#include + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PROGMEM_WWW +#include +#endif + +class ESP8266React { + public: + ESP8266React(AsyncWebServer* server); + + void begin(); + void loop(); + + FS* getFS() { + return &ESPFS; + } + + SecurityManager* getSecurityManager() { + return &_securitySettingsService; + } + +#if FT_ENABLED(FT_SECURITY) + StatefulService* getSecuritySettingsService() { + return &_securitySettingsService; + } +#endif + + StatefulService* getWiFiSettingsService() { + return &_wifiSettingsService; + } + + StatefulService* getAPSettingsService() { + return &_apSettingsService; + } + +#if FT_ENABLED(FT_NTP) + StatefulService* getNTPSettingsService() { + return &_ntpSettingsService; + } +#endif + +#if FT_ENABLED(FT_OTA) + StatefulService* getOTASettingsService() { + return &_otaSettingsService; + } +#endif + +#if FT_ENABLED(FT_MQTT) + StatefulService* getMqttSettingsService() { + return &_mqttSettingsService; + } + + AsyncMqttClient* getMqttClient() { + return _mqttSettingsService.getMqttClient(); + } +#endif + + void factoryReset() { + _factoryResetService.factoryReset(); + } + + private: + FeaturesService _featureService; + SecuritySettingsService _securitySettingsService; + WiFiSettingsService _wifiSettingsService; + WiFiScanner _wifiScanner; + WiFiStatus _wifiStatus; + APSettingsService _apSettingsService; + APStatus _apStatus; +#if FT_ENABLED(FT_NTP) + NTPSettingsService _ntpSettingsService; + NTPStatus _ntpStatus; +#endif +#if FT_ENABLED(FT_OTA) + OTASettingsService _otaSettingsService; +#endif +#if FT_ENABLED(FT_UPLOAD_FIRMWARE) + UploadFirmwareService _uploadFirmwareService; +#endif +#if FT_ENABLED(FT_MQTT) + MqttSettingsService _mqttSettingsService; + MqttStatus _mqttStatus; +#endif +#if FT_ENABLED(FT_SECURITY) + AuthenticationService _authenticationService; +#endif + RestartService _restartService; + FactoryResetService _factoryResetService; + SystemStatus _systemStatus; +}; + +#endif diff --git a/lib/framework/ESPFS.h b/lib/framework/ESPFS.h new file mode 100644 index 0000000..7585f02 --- /dev/null +++ b/lib/framework/ESPFS.h @@ -0,0 +1,7 @@ +#ifdef ESP32 +#include +#define ESPFS SPIFFS +#elif defined(ESP8266) +#include +#define ESPFS LittleFS +#endif diff --git a/lib/framework/FSPersistence.h b/lib/framework/FSPersistence.h new file mode 100644 index 0000000..f3d9573 --- /dev/null +++ b/lib/framework/FSPersistence.h @@ -0,0 +1,100 @@ +#ifndef FSPersistence_h +#define FSPersistence_h + +#include +#include + +template +class FSPersistence { + public: + FSPersistence(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + FS* fs, + const char* filePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _fs(fs), + _filePath(filePath), + _bufferSize(bufferSize), + _updateHandlerId(0) { + enableUpdateHandler(); + } + + void readFromFS() { + File settingsFile = _fs->open(_filePath, "r"); + + if (settingsFile) { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + DeserializationError error = deserializeJson(jsonDocument, settingsFile); + if (error == DeserializationError::Ok && jsonDocument.is()) { + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + settingsFile.close(); + return; + } + settingsFile.close(); + } + + // If we reach here we have not been successful in loading the config and hard-coded defaults are now applied. + // The settings are then written back to the file system so the defaults persist between resets. This last step is + // required as in some cases defaults contain randomly generated values which would otherwise be modified on reset. + applyDefaults(); + writeToFS(); + } + + bool writeToFS() { + // create and populate a new json object + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.to(); + _statefulService->read(jsonObject, _stateReader); + + // serialize it to filesystem + File settingsFile = _fs->open(_filePath, "w"); + + // failed to open file, return false + if (!settingsFile) { + return false; + } + + // serialize the data to the file + serializeJson(jsonDocument, settingsFile); + settingsFile.close(); + return true; + } + + void disableUpdateHandler() { + if (_updateHandlerId) { + _statefulService->removeUpdateHandler(_updateHandlerId); + _updateHandlerId = 0; + } + } + + void enableUpdateHandler() { + if (!_updateHandlerId) { + _updateHandlerId = _statefulService->addUpdateHandler([&](const String& originId) { writeToFS(); }); + } + } + + private: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService* _statefulService; + FS* _fs; + const char* _filePath; + size_t _bufferSize; + update_handler_id_t _updateHandlerId; + + protected: + // We assume the updater supplies sensible defaults if an empty object + // is supplied, this virtual function allows that to be changed. + virtual void applyDefaults() { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); + JsonObject jsonObject = jsonDocument.as(); + _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + } +}; + +#endif // end FSPersistence diff --git a/lib/framework/FactoryResetService.cpp b/lib/framework/FactoryResetService.cpp new file mode 100644 index 0000000..9742207 --- /dev/null +++ b/lib/framework/FactoryResetService.cpp @@ -0,0 +1,37 @@ +#include + +using namespace std::placeholders; + +FactoryResetService::FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : fs(fs) { + server->on(FACTORY_RESET_SERVICE_PATH, + HTTP_POST, + securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), + AuthenticationPredicates::IS_ADMIN)); +} + +void FactoryResetService::handleRequest(AsyncWebServerRequest* request) { + request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this)); + request->send(200); +} + +/** + * Delete function assumes that all files are stored flat, within the config directory. + */ +void FactoryResetService::factoryReset() { +#ifdef ESP32 + File root = fs->open(FS_CONFIG_DIRECTORY); + File file; + while (file = root.openNextFile()) { + fs->remove(file.name()); + } +#elif defined(ESP8266) + Dir configDirectory = fs->openDir(FS_CONFIG_DIRECTORY); + while (configDirectory.next()) { + String path = FS_CONFIG_DIRECTORY; + path.concat("/"); + path.concat(configDirectory.fileName()); + fs->remove(path); + } +#endif + RestartService::restartNow(); +} diff --git a/lib/framework/FactoryResetService.h b/lib/framework/FactoryResetService.h new file mode 100644 index 0000000..2336e6f --- /dev/null +++ b/lib/framework/FactoryResetService.h @@ -0,0 +1,32 @@ +#ifndef FactoryResetService_h +#define FactoryResetService_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include + +#define FS_CONFIG_DIRECTORY "/config" +#define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset" + +class FactoryResetService { + FS* fs; + + public: + FactoryResetService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + + void factoryReset(); + + private: + void handleRequest(AsyncWebServerRequest* request); +}; + +#endif // end FactoryResetService_h diff --git a/lib/framework/Features.h b/lib/framework/Features.h new file mode 100644 index 0000000..2de82c5 --- /dev/null +++ b/lib/framework/Features.h @@ -0,0 +1,37 @@ +#ifndef Features_h +#define Features_h + +#define FT_ENABLED(feature) feature + +// project feature off by default +#ifndef FT_PROJECT +#define FT_PROJECT 0 +#endif + +// security feature on by default +#ifndef FT_SECURITY +#define FT_SECURITY 1 +#endif + +// mqtt feature on by default +#ifndef FT_MQTT +#define FT_MQTT 1 +#endif + +// ntp feature on by default +#ifndef FT_NTP +#define FT_NTP 1 +#endif + +// mqtt feature on by default +#ifndef FT_OTA +#define FT_OTA 1 +#endif + +// upload firmware feature off by default +#ifndef FT_UPLOAD_FIRMWARE +#define FT_UPLOAD_FIRMWARE 0 +#endif + + +#endif diff --git a/lib/framework/FeaturesService.cpp b/lib/framework/FeaturesService.cpp new file mode 100644 index 0000000..095f1f2 --- /dev/null +++ b/lib/framework/FeaturesService.cpp @@ -0,0 +1,42 @@ +#include + +FeaturesService::FeaturesService(AsyncWebServer* server) { + server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1)); +} + +void FeaturesService::features(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); + JsonObject root = response->getRoot(); +#if FT_ENABLED(FT_PROJECT) + root["project"] = true; +#else + root["project"] = false; +#endif +#if FT_ENABLED(FT_SECURITY) + root["security"] = true; +#else + root["security"] = false; +#endif +#if FT_ENABLED(FT_MQTT) + root["mqtt"] = true; +#else + root["mqtt"] = false; +#endif +#if FT_ENABLED(FT_NTP) + root["ntp"] = true; +#else + root["ntp"] = false; +#endif +#if FT_ENABLED(FT_OTA) + root["ota"] = true; +#else + root["ota"] = false; +#endif +#if FT_ENABLED(FT_UPLOAD_FIRMWARE) + root["upload_firmware"] = true; +#else + root["upload_firmware"] = false; +#endif + response->setLength(); + request->send(response); +} diff --git a/lib/framework/FeaturesService.h b/lib/framework/FeaturesService.h new file mode 100644 index 0000000..867101e --- /dev/null +++ b/lib/framework/FeaturesService.h @@ -0,0 +1,29 @@ +#ifndef FeaturesService_h +#define FeaturesService_h + +#include + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include + +#define MAX_FEATURES_SIZE 256 +#define FEATURES_SERVICE_PATH "/rest/features" + +class FeaturesService { + public: + FeaturesService(AsyncWebServer* server); + + private: + void features(AsyncWebServerRequest* request); +}; + +#endif diff --git a/lib/framework/HttpEndpoint.h b/lib/framework/HttpEndpoint.h new file mode 100644 index 0000000..f45e716 --- /dev/null +++ b/lib/framework/HttpEndpoint.h @@ -0,0 +1,165 @@ +#ifndef HttpEndpoint_h +#define HttpEndpoint_h + +#include + +#include +#include + +#include +#include + +#define HTTP_ENDPOINT_ORIGIN_ID "http" + +template +class HttpGetEndpoint { + public: + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { + server->on(servicePath.c_str(), + HTTP_GET, + securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1), + authenticationPredicate)); + } + + HttpGetEndpoint(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { + server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1)); + } + + protected: + JsonStateReader _stateReader; + StatefulService* _statefulService; + size_t _bufferSize; + + void fetchSettings(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); + JsonObject jsonObject = response->getRoot().to(); + _statefulService->read(jsonObject, _stateReader); + + response->setLength(); + request->send(response); + } +}; + +template +class HttpPostEndpoint { + public: + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _updateHandler( + servicePath, + securityManager->wrapCallback( + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + authenticationPredicate), + bufferSize), + _bufferSize(bufferSize) { + _updateHandler.setMethod(HTTP_POST); + server->addHandler(&_updateHandler); + } + + HttpPostEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + _stateReader(stateReader), + _stateUpdater(stateUpdater), + _statefulService(statefulService), + _updateHandler(servicePath, + std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2), + bufferSize), + _bufferSize(bufferSize) { + _updateHandler.setMethod(HTTP_POST); + server->addHandler(&_updateHandler); + } + + protected: + JsonStateReader _stateReader; + JsonStateUpdater _stateUpdater; + StatefulService* _statefulService; + AsyncCallbackJsonWebHandler _updateHandler; + size_t _bufferSize; + + void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) { + if (!json.is()) { + request->send(400); + return; + } + JsonObject jsonObject = json.as(); + StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); + if (outcome == StateUpdateResult::ERROR) { + request->send(400); + return; + } + if (outcome == StateUpdateResult::CHANGED) { + request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); }); + } + AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize); + jsonObject = response->getRoot().to(); + _statefulService->read(jsonObject, _stateReader); + response->setLength(); + request->send(response); + } +}; + +template +class HttpEndpoint : public HttpGetEndpoint, public HttpPostEndpoint { + public: + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + HttpGetEndpoint(stateReader, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize), + HttpPostEndpoint(stateReader, + stateUpdater, + statefulService, + server, + servicePath, + securityManager, + authenticationPredicate, + bufferSize) { + } + + HttpEndpoint(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const String& servicePath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + HttpGetEndpoint(stateReader, statefulService, server, servicePath, bufferSize), + HttpPostEndpoint(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) { + } +}; + +#endif // end HttpEndpoint diff --git a/lib/framework/JsonUtils.h b/lib/framework/JsonUtils.h new file mode 100644 index 0000000..0c40898 --- /dev/null +++ b/lib/framework/JsonUtils.h @@ -0,0 +1,29 @@ +#ifndef JsonUtils_h +#define JsonUtils_h + +#include +#include +#include + +class JsonUtils { + public: + static void readIP(JsonObject& root, const String& key, IPAddress& ip, const String& def) { + IPAddress defaultIp = {}; + if (!defaultIp.fromString(def)) { + defaultIp = INADDR_NONE; + } + readIP(root, key, ip, defaultIp); + } + static void readIP(JsonObject& root, const String& key, IPAddress& ip, const IPAddress& defaultIp = INADDR_NONE) { + if (!root[key].is() || !ip.fromString(root[key].as())) { + ip = defaultIp; + } + } + static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) { + if (ip != INADDR_NONE) { + root[key] = ip.toString(); + } + } +}; + +#endif // end JsonUtils diff --git a/lib/framework/MqttPubSub.h b/lib/framework/MqttPubSub.h new file mode 100644 index 0000000..c3ed3f1 --- /dev/null +++ b/lib/framework/MqttPubSub.h @@ -0,0 +1,167 @@ +#ifndef MqttPubSub_h +#define MqttPubSub_h + +#include +#include + +#define MQTT_ORIGIN_ID "mqtt" + +template +class MqttConnector { + protected: + StatefulService* _statefulService; + AsyncMqttClient* _mqttClient; + size_t _bufferSize; + + MqttConnector(StatefulService* statefulService, AsyncMqttClient* mqttClient, size_t bufferSize) : + _statefulService(statefulService), _mqttClient(mqttClient), _bufferSize(bufferSize) { + _mqttClient->onConnect(std::bind(&MqttConnector::onConnect, this)); + } + + virtual void onConnect() = 0; + + public: + inline AsyncMqttClient* getMqttClient() const { + return _mqttClient; + } +}; + +template +class MqttPub : virtual public MqttConnector { + public: + MqttPub(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncMqttClient* mqttClient, + const String& pubTopic = "", + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + MqttConnector(statefulService, mqttClient, bufferSize), _stateReader(stateReader), _pubTopic(pubTopic) { + MqttConnector::_statefulService->addUpdateHandler([&](const String& originId) { publish(); }, false); + } + + void setPubTopic(const String& pubTopic) { + _pubTopic = pubTopic; + publish(); + } + + protected: + virtual void onConnect() { + publish(); + } + + private: + JsonStateReader _stateReader; + String _pubTopic; + + void publish() { + if (_pubTopic.length() > 0 && MqttConnector::_mqttClient->connected()) { + // serialize to json doc + DynamicJsonDocument json(MqttConnector::_bufferSize); + JsonObject jsonObject = json.to(); + MqttConnector::_statefulService->read(jsonObject, _stateReader); + + // serialize to string + String payload; + serializeJson(json, payload); + + // publish the payload + MqttConnector::_mqttClient->publish(_pubTopic.c_str(), 0, false, payload.c_str()); + } + } +}; + +template +class MqttSub : virtual public MqttConnector { + public: + MqttSub(JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncMqttClient* mqttClient, + const String& subTopic = "", + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + MqttConnector(statefulService, mqttClient, bufferSize), _stateUpdater(stateUpdater), _subTopic(subTopic) { + MqttConnector::_mqttClient->onMessage(std::bind(&MqttSub::onMqttMessage, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); + } + + void setSubTopic(const String& subTopic) { + if (!_subTopic.equals(subTopic)) { + // unsubscribe from the existing topic if one was set + if (_subTopic.length() > 0) { + MqttConnector::_mqttClient->unsubscribe(_subTopic.c_str()); + } + // set the new topic and re-configure the subscription + _subTopic = subTopic; + subscribe(); + } + } + + protected: + virtual void onConnect() { + subscribe(); + } + + private: + JsonStateUpdater _stateUpdater; + String _subTopic; + + void subscribe() { + if (_subTopic.length() > 0) { + MqttConnector::_mqttClient->subscribe(_subTopic.c_str(), 2); + } + } + + void onMqttMessage(char* topic, + char* payload, + AsyncMqttClientMessageProperties properties, + size_t len, + size_t index, + size_t total) { + // we only care about the topic we are watching in this class + if (strcmp(_subTopic.c_str(), topic)) { + return; + } + + // deserialize from string + DynamicJsonDocument json(MqttConnector::_bufferSize); + DeserializationError error = deserializeJson(json, payload, len); + if (!error && json.is()) { + JsonObject jsonObject = json.as(); + MqttConnector::_statefulService->update(jsonObject, _stateUpdater, MQTT_ORIGIN_ID); + } + } +}; + +template +class MqttPubSub : public MqttPub, public MqttSub { + public: + MqttPubSub(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncMqttClient* mqttClient, + const String& pubTopic = "", + const String& subTopic = "", + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + MqttConnector(statefulService, mqttClient, bufferSize), + MqttPub(stateReader, statefulService, mqttClient, pubTopic, bufferSize), + MqttSub(stateUpdater, statefulService, mqttClient, subTopic, bufferSize) { + } + + public: + void configureTopics(const String& pubTopic, const String& subTopic) { + MqttSub::setSubTopic(subTopic); + MqttPub::setPubTopic(pubTopic); + } + + protected: + void onConnect() { + MqttSub::onConnect(); + MqttPub::onConnect(); + } +}; + +#endif // end MqttPubSub diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp new file mode 100644 index 0000000..a9932ae --- /dev/null +++ b/lib/framework/MqttSettingsService.cpp @@ -0,0 +1,161 @@ +#include + +/** + * Retains a copy of the cstr provided in the pointer provided using dynamic allocation. + * + * Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr. + */ +static char* retainCstr(const char* cstr, char** ptr) { + // free up previously retained value if exists + free(*ptr); + *ptr = nullptr; + + // dynamically allocate and copy cstr (if non null) + if (cstr != nullptr) { + *ptr = (char*)malloc(strlen(cstr) + 1); + strcpy(*ptr, cstr); + } + + // return reference to pointer for convenience + return *ptr; +} + +MqttSettingsService::MqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE), + _retainedHost(nullptr), + _retainedClientId(nullptr), + _retainedUsername(nullptr), + _retainedPassword(nullptr), + _reconfigureMqtt(false), + _disconnectedAt(0), + _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED), + _mqttClient() { +#ifdef ESP32 + WiFi.onEvent( + std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); +#elif defined(ESP8266) + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1)); +#endif + _mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, std::placeholders::_1)); + _mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, std::placeholders::_1)); + addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +MqttSettingsService::~MqttSettingsService() { +} + +void MqttSettingsService::begin() { + _fsPersistence.readFromFS(); +} + +void MqttSettingsService::loop() { + if (_reconfigureMqtt || (_disconnectedAt && (unsigned long)(millis() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { + // reconfigure MQTT client + configureMqtt(); + + // clear the reconnection flags + _reconfigureMqtt = false; + _disconnectedAt = 0; + } +} + +bool MqttSettingsService::isEnabled() { + return _state.enabled; +} + +bool MqttSettingsService::isConnected() { + return _mqttClient.connected(); +} + +const char* MqttSettingsService::getClientId() { + return _mqttClient.getClientId(); +} + +AsyncMqttClientDisconnectReason MqttSettingsService::getDisconnectReason() { + return _disconnectReason; +} + +AsyncMqttClient* MqttSettingsService::getMqttClient() { + return &_mqttClient; +} + +void MqttSettingsService::onMqttConnect(bool sessionPresent) { + Serial.print(F("Connected to MQTT, ")); + if (sessionPresent) { + Serial.println(F("with persistent session")); + } else { + Serial.println(F("without persistent session")); + } +} + +void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { + Serial.print(F("Disconnected from MQTT reason: ")); + Serial.println((uint8_t)reason); + _disconnectReason = reason; + _disconnectedAt = millis(); +} + +void MqttSettingsService::onConfigUpdated() { + _reconfigureMqtt = true; + _disconnectedAt = 0; +} + +#ifdef ESP32 +void MqttSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { + if (_state.enabled) { + Serial.println(F("WiFi connection dropped, starting MQTT client.")); + onConfigUpdated(); + } +} + +void MqttSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { + if (_state.enabled) { + Serial.println(F("WiFi connection dropped, stopping MQTT client.")); + onConfigUpdated(); + } +} +#elif defined(ESP8266) +void MqttSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { + if (_state.enabled) { + Serial.println(F("WiFi connection dropped, starting MQTT client.")); + onConfigUpdated(); + } +} + +void MqttSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { + if (_state.enabled) { + Serial.println(F("WiFi connection dropped, stopping MQTT client.")); + onConfigUpdated(); + } +} +#endif + +void MqttSettingsService::configureMqtt() { + // disconnect if currently connected + _mqttClient.disconnect(); + + // only connect if WiFi is connected and MQTT is enabled + if (_state.enabled && WiFi.isConnected()) { + Serial.println(F("Connecting to MQTT...")); + _mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port); + if (_state.username.length() > 0) { + _mqttClient.setCredentials( + retainCstr(_state.username.c_str(), &_retainedUsername), + retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); + } else { + _mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); + } + _mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); + _mqttClient.setKeepAlive(_state.keepAlive); + _mqttClient.setCleanSession(_state.cleanSession); + _mqttClient.setMaxTopicLength(_state.maxTopicLength); + _mqttClient.connect(); + } +} diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h new file mode 100644 index 0000000..a011743 --- /dev/null +++ b/lib/framework/MqttSettingsService.h @@ -0,0 +1,148 @@ +#ifndef MqttSettingsService_h +#define MqttSettingsService_h + +#include +#include +#include +#include +#include + +#ifndef FACTORY_MQTT_ENABLED +#define FACTORY_MQTT_ENABLED false +#endif + +#ifndef FACTORY_MQTT_HOST +#define FACTORY_MQTT_HOST "test.mosquitto.org" +#endif + +#ifndef FACTORY_MQTT_PORT +#define FACTORY_MQTT_PORT 1883 +#endif + +#ifndef FACTORY_MQTT_USERNAME +#define FACTORY_MQTT_USERNAME "" +#endif + +#ifndef FACTORY_MQTT_PASSWORD +#define FACTORY_MQTT_PASSWORD "" +#endif + +#ifndef FACTORY_MQTT_CLIENT_ID +#define FACTORY_MQTT_CLIENT_ID "#{platform}-#{unique_id}" +#endif + +#ifndef FACTORY_MQTT_KEEP_ALIVE +#define FACTORY_MQTT_KEEP_ALIVE 16 +#endif + +#ifndef FACTORY_MQTT_CLEAN_SESSION +#define FACTORY_MQTT_CLEAN_SESSION true +#endif + +#ifndef FACTORY_MQTT_MAX_TOPIC_LENGTH +#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128 +#endif + +#define MQTT_SETTINGS_FILE "/config/mqttSettings.json" +#define MQTT_SETTINGS_SERVICE_PATH "/rest/mqttSettings" + +#define MQTT_RECONNECTION_DELAY 5000 + +class MqttSettings { + public: + // host and port - if enabled + bool enabled; + String host; + uint16_t port; + + // username and password + String username; + String password; + + // client id settings + String clientId; + + // connection settings + uint16_t keepAlive; + bool cleanSession; + uint16_t maxTopicLength; + + static void read(MqttSettings& settings, JsonObject& root) { + root["enabled"] = settings.enabled; + root["host"] = settings.host; + root["port"] = settings.port; + root["username"] = settings.username; + root["password"] = settings.password; + root["client_id"] = settings.clientId; + root["keep_alive"] = settings.keepAlive; + root["clean_session"] = settings.cleanSession; + root["max_topic_length"] = settings.maxTopicLength; + } + + static StateUpdateResult update(JsonObject& root, MqttSettings& settings) { + settings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; + settings.host = root["host"] | FACTORY_MQTT_HOST; + settings.port = root["port"] | FACTORY_MQTT_PORT; + settings.username = root["username"] | SettingValue::format(FACTORY_MQTT_USERNAME); + settings.password = root["password"] | FACTORY_MQTT_PASSWORD; + settings.clientId = root["client_id"] | SettingValue::format(FACTORY_MQTT_CLIENT_ID); + settings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE; + settings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; + settings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH; + return StateUpdateResult::CHANGED; + } +}; + +class MqttSettingsService : public StatefulService { + public: + MqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + ~MqttSettingsService(); + + void begin(); + void loop(); + bool isEnabled(); + bool isConnected(); + const char* getClientId(); + AsyncMqttClientDisconnectReason getDisconnectReason(); + AsyncMqttClient* getMqttClient(); + + protected: + void onConfigUpdated(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + + // Pointers to hold retained copies of the mqtt client connection strings. + // This is required as AsyncMqttClient holds refrences to the supplied connection strings. + char* _retainedHost; + char* _retainedClientId; + char* _retainedUsername; + char* _retainedPassword; + + // variable to help manage connection + bool _reconfigureMqtt; + unsigned long _disconnectedAt; + + // connection status + AsyncMqttClientDisconnectReason _disconnectReason; + + // the MQTT client instance + AsyncMqttClient _mqttClient; + +#ifdef ESP32 + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ESP8266) + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; + void onStationModeGotIP(const WiFiEventStationModeGotIP& event); + void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); +#endif + + void onMqttConnect(bool sessionPresent); + void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); + void configureMqtt(); +}; + +#endif // end MqttSettingsService_h diff --git a/lib/framework/MqttStatus.cpp b/lib/framework/MqttStatus.cpp new file mode 100644 index 0000000..8f5bac3 --- /dev/null +++ b/lib/framework/MqttStatus.cpp @@ -0,0 +1,24 @@ +#include + +MqttStatus::MqttStatus(AsyncWebServer* server, + MqttSettingsService* mqttSettingsService, + SecurityManager* securityManager) : + _mqttSettingsService(mqttSettingsService) { + server->on(MQTT_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); +} + +void MqttStatus::mqttStatus(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_MQTT_STATUS_SIZE); + JsonObject root = response->getRoot(); + + root["enabled"] = _mqttSettingsService->isEnabled(); + root["connected"] = _mqttSettingsService->isConnected(); + root["client_id"] = _mqttSettingsService->getClientId(); + root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason(); + + response->setLength(); + request->send(response); +} diff --git a/lib/framework/MqttStatus.h b/lib/framework/MqttStatus.h new file mode 100644 index 0000000..a726d3b --- /dev/null +++ b/lib/framework/MqttStatus.h @@ -0,0 +1,31 @@ +#ifndef MqttStatus_h +#define MqttStatus_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define MAX_MQTT_STATUS_SIZE 1024 +#define MQTT_STATUS_SERVICE_PATH "/rest/mqttStatus" + +class MqttStatus { + public: + MqttStatus(AsyncWebServer* server, MqttSettingsService* mqttSettingsService, SecurityManager* securityManager); + + private: + MqttSettingsService* _mqttSettingsService; + + void mqttStatus(AsyncWebServerRequest* request); +}; + +#endif // end MqttStatus_h diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp new file mode 100644 index 0000000..420db7f --- /dev/null +++ b/lib/framework/NTPSettingsService.cpp @@ -0,0 +1,90 @@ +#include + +NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE), + _timeHandler(TIME_PATH, + securityManager->wrapCallback( + std::bind(&NTPSettingsService::configureTime, this, std::placeholders::_1, std::placeholders::_2), + AuthenticationPredicates::IS_ADMIN)) { + _timeHandler.setMethod(HTTP_POST); + _timeHandler.setMaxContentLength(MAX_TIME_SIZE); + server->addHandler(&_timeHandler); +#ifdef ESP32 + WiFi.onEvent( + std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); +#elif defined(ESP8266) + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1)); +#endif + addUpdateHandler([&](const String& originId) { configureNTP(); }, false); +} + +void NTPSettingsService::begin() { + _fsPersistence.readFromFS(); + configureNTP(); +} + +#ifdef ESP32 +void NTPSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.println(F("Got IP address, starting NTP Synchronization")); + configureNTP(); +} + +void NTPSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.println(F("WiFi connection dropped, stopping NTP.")); + configureNTP(); +} +#elif defined(ESP8266) +void NTPSettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { + Serial.println(F("Got IP address, starting NTP Synchronization")); + configureNTP(); +} + +void NTPSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { + Serial.println(F("WiFi connection dropped, stopping NTP.")); + configureNTP(); +} +#endif + +void NTPSettingsService::configureNTP() { + if (WiFi.isConnected() && _state.enabled) { + Serial.println(F("Starting NTP...")); +#ifdef ESP32 + configTzTime(_state.tzFormat.c_str(), _state.server.c_str()); +#elif defined(ESP8266) + configTime(_state.tzFormat.c_str(), _state.server.c_str()); +#endif + } else { +#ifdef ESP32 + setenv("TZ", _state.tzFormat.c_str(), 1); + tzset(); +#elif defined(ESP8266) + setTZ(_state.tzFormat.c_str()); +#endif + sntp_stop(); + } +} + +void NTPSettingsService::configureTime(AsyncWebServerRequest* request, JsonVariant& json) { + if (!sntp_enabled() && json.is()) { + String timeUtc = json["time_utc"]; + struct tm tm = {0}; + char* s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); + if (s != nullptr) { + time_t time = mktime(&tm); + struct timeval now = {.tv_sec = time}; + settimeofday(&now, nullptr); + AsyncWebServerResponse* response = request->beginResponse(200); + request->send(response); + return; + } + } + AsyncWebServerResponse* response = request->beginResponse(400); + request->send(response); +} diff --git a/lib/framework/NTPSettingsService.h b/lib/framework/NTPSettingsService.h new file mode 100644 index 0000000..bf25ca4 --- /dev/null +++ b/lib/framework/NTPSettingsService.h @@ -0,0 +1,84 @@ +#ifndef NTPSettingsService_h +#define NTPSettingsService_h + +#include +#include + +#include +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif + +#ifndef FACTORY_NTP_ENABLED +#define FACTORY_NTP_ENABLED true +#endif + +#ifndef FACTORY_NTP_TIME_ZONE_LABEL +#define FACTORY_NTP_TIME_ZONE_LABEL "Europe/London" +#endif + +#ifndef FACTORY_NTP_TIME_ZONE_FORMAT +#define FACTORY_NTP_TIME_ZONE_FORMAT "GMT0BST,M3.5.0/1,M10.5.0" +#endif + +#ifndef FACTORY_NTP_SERVER +#define FACTORY_NTP_SERVER "time.google.com" +#endif + +#define NTP_SETTINGS_FILE "/config/ntpSettings.json" +#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings" + +#define MAX_TIME_SIZE 256 +#define TIME_PATH "/rest/time" + +class NTPSettings { + public: + bool enabled; + String tzLabel; + String tzFormat; + String server; + + static void read(NTPSettings& settings, JsonObject& root) { + root["enabled"] = settings.enabled; + root["server"] = settings.server; + root["tz_label"] = settings.tzLabel; + root["tz_format"] = settings.tzFormat; + } + + static StateUpdateResult update(JsonObject& root, NTPSettings& settings) { + settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED; + settings.server = root["server"] | FACTORY_NTP_SERVER; + settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL; + settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT; + return StateUpdateResult::CHANGED; + } +}; + +class NTPSettingsService : public StatefulService { + public: + NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + + void begin(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + AsyncCallbackJsonWebHandler _timeHandler; + +#ifdef ESP32 + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ESP8266) + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; + + void onStationModeGotIP(const WiFiEventStationModeGotIP& event); + void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); +#endif + void configureNTP(); + void configureTime(AsyncWebServerRequest* request, JsonVariant& json); +}; + +#endif // end NTPSettingsService_h diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp new file mode 100644 index 0000000..2275d2f --- /dev/null +++ b/lib/framework/NTPStatus.cpp @@ -0,0 +1,40 @@ +#include + +NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { + server->on(NTP_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); +} + +String toISOString(tm* time, bool incOffset) { + char time_string[25]; + strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time); + return String(time_string); +} + +void NTPStatus::ntpStatus(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); + JsonObject root = response->getRoot(); + + // grab the current instant in unix seconds + time_t now = time(nullptr); + + // only provide enabled/disabled status for now + root["status"] = sntp_enabled() ? 1 : 0; + + // the current time in UTC + root["time_utc"] = toISOString(gmtime(&now), false); + + // local time as ISO String with TZ + root["time_local"] = toISOString(localtime(&now), true); + + // the sntp server name + root["server"] = sntp_getservername(0); + + // device uptime in seconds + root["uptime"] = millis() / 1000; + + response->setLength(); + request->send(response); +} diff --git a/lib/framework/NTPStatus.h b/lib/framework/NTPStatus.h new file mode 100644 index 0000000..7bb9180 --- /dev/null +++ b/lib/framework/NTPStatus.h @@ -0,0 +1,31 @@ +#ifndef NTPStatus_h +#define NTPStatus_h + +#include +#ifdef ESP32 +#include +#include +#include +#elif defined(ESP8266) +#include +#include +#include +#endif + +#include +#include +#include +#include + +#define MAX_NTP_STATUS_SIZE 1024 +#define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" + +class NTPStatus { + public: + NTPStatus(AsyncWebServer* server, SecurityManager* securityManager); + + private: + void ntpStatus(AsyncWebServerRequest* request); +}; + +#endif // end NTPStatus_h diff --git a/lib/framework/OTASettingsService.cpp b/lib/framework/OTASettingsService.cpp new file mode 100644 index 0000000..073f78b --- /dev/null +++ b/lib/framework/OTASettingsService.cpp @@ -0,0 +1,71 @@ +#include + +OTASettingsService::OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE), + _arduinoOTA(nullptr) { +#ifdef ESP32 + WiFi.onEvent(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); +#elif defined(ESP8266) + _onStationModeGotIPHandler = + WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1)); +#endif + addUpdateHandler([&](const String& originId) { configureArduinoOTA(); }, false); +} + +void OTASettingsService::begin() { + _fsPersistence.readFromFS(); + configureArduinoOTA(); +} + +void OTASettingsService::loop() { + if (_state.enabled && _arduinoOTA) { + _arduinoOTA->handle(); + } +} + +void OTASettingsService::configureArduinoOTA() { + if (_arduinoOTA) { +#ifdef ESP32 + _arduinoOTA->end(); +#endif + delete _arduinoOTA; + _arduinoOTA = nullptr; + } + if (_state.enabled) { + Serial.println(F("Starting OTA Update Service...")); + _arduinoOTA = new ArduinoOTAClass; + _arduinoOTA->setPort(_state.port); + _arduinoOTA->setPassword(_state.password.c_str()); + _arduinoOTA->onStart([]() { Serial.println(F("Starting")); }); + _arduinoOTA->onEnd([]() { Serial.println(F("\r\nEnd")); }); + _arduinoOTA->onProgress([](unsigned int progress, unsigned int total) { + Serial.printf_P(PSTR("Progress: %u%%\r\n"), (progress / (total / 100))); + }); + _arduinoOTA->onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) + Serial.println(F("Auth Failed")); + else if (error == OTA_BEGIN_ERROR) + Serial.println(F("Begin Failed")); + else if (error == OTA_CONNECT_ERROR) + Serial.println(F("Connect Failed")); + else if (error == OTA_RECEIVE_ERROR) + Serial.println(F("Receive Failed")); + else if (error == OTA_END_ERROR) + Serial.println(F("End Failed")); + }); + _arduinoOTA->begin(); + } +} + +#ifdef ESP32 +void OTASettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { + configureArduinoOTA(); +} +#elif defined(ESP8266) +void OTASettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { + configureArduinoOTA(); +} +#endif diff --git a/lib/framework/OTASettingsService.h b/lib/framework/OTASettingsService.h new file mode 100644 index 0000000..c8d8609 --- /dev/null +++ b/lib/framework/OTASettingsService.h @@ -0,0 +1,72 @@ +#ifndef OTASettingsService_h +#define OTASettingsService_h + +#include +#include + +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif + +#include +#include + +#ifndef FACTORY_OTA_PORT +#define FACTORY_OTA_PORT 8266 +#endif + +#ifndef FACTORY_OTA_PASSWORD +#define FACTORY_OTA_PASSWORD "esp-react" +#endif + +#ifndef FACTORY_OTA_ENABLED +#define FACTORY_OTA_ENABLED true +#endif + +#define OTA_SETTINGS_FILE "/config/otaSettings.json" +#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings" + +class OTASettings { + public: + bool enabled; + int port; + String password; + + static void read(OTASettings& settings, JsonObject& root) { + root["enabled"] = settings.enabled; + root["port"] = settings.port; + root["password"] = settings.password; + } + + static StateUpdateResult update(JsonObject& root, OTASettings& settings) { + settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED; + settings.port = root["port"] | FACTORY_OTA_PORT; + settings.password = root["password"] | FACTORY_OTA_PASSWORD; + return StateUpdateResult::CHANGED; + } +}; + +class OTASettingsService : public StatefulService { + public: + OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + + void begin(); + void loop(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + ArduinoOTAClass* _arduinoOTA; + + void configureArduinoOTA(); +#ifdef ESP32 + void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ESP8266) + WiFiEventHandler _onStationModeGotIPHandler; + void onStationModeGotIP(const WiFiEventStationModeGotIP& event); +#endif +}; + +#endif // end OTASettingsService_h diff --git a/lib/framework/RestartService.cpp b/lib/framework/RestartService.cpp new file mode 100644 index 0000000..9036e40 --- /dev/null +++ b/lib/framework/RestartService.cpp @@ -0,0 +1,13 @@ +#include + +RestartService::RestartService(AsyncWebServer* server, SecurityManager* securityManager) { + server->on(RESTART_SERVICE_PATH, + HTTP_POST, + securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); +} + +void RestartService::restart(AsyncWebServerRequest* request) { + request->onDisconnect(RestartService::restartNow); + request->send(200); +} diff --git a/lib/framework/RestartService.h b/lib/framework/RestartService.h new file mode 100644 index 0000000..45a1008 --- /dev/null +++ b/lib/framework/RestartService.h @@ -0,0 +1,31 @@ +#ifndef RestartService_h +#define RestartService_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include + +#define RESTART_SERVICE_PATH "/rest/restart" + +class RestartService { + public: + RestartService(AsyncWebServer* server, SecurityManager* securityManager); + + static void restartNow() { + WiFi.disconnect(true); + delay(500); + ESP.restart(); + } + + private: + void restart(AsyncWebServerRequest* request); +}; + +#endif // end RestartService_h diff --git a/lib/framework/SecurityManager.h b/lib/framework/SecurityManager.h new file mode 100644 index 0000000..2302deb --- /dev/null +++ b/lib/framework/SecurityManager.h @@ -0,0 +1,97 @@ +#ifndef SecurityManager_h +#define SecurityManager_h + +#include +#include +#include +#include +#include + +#define ACCESS_TOKEN_PARAMATER "access_token" + +#define AUTHORIZATION_HEADER "Authorization" +#define AUTHORIZATION_HEADER_PREFIX "Bearer " +#define AUTHORIZATION_HEADER_PREFIX_LEN 7 + +#define MAX_JWT_SIZE 128 + +class User { + public: + String username; + String password; + bool admin; + + public: + User(String username, String password, bool admin) : username(username), password(password), admin(admin) { + } +}; + +class Authentication { + public: + User* user; + boolean authenticated; + + public: + Authentication(User& user) : user(new User(user)), authenticated(true) { + } + Authentication() : user(nullptr), authenticated(false) { + } + ~Authentication() { + delete (user); + } +}; + +typedef std::function AuthenticationPredicate; + +class AuthenticationPredicates { + public: + static bool NONE_REQUIRED(Authentication& authentication) { + return true; + }; + static bool IS_AUTHENTICATED(Authentication& authentication) { + return authentication.authenticated; + }; + static bool IS_ADMIN(Authentication& authentication) { + return authentication.authenticated && authentication.user->admin; + }; +}; + +class SecurityManager { + public: +#if FT_ENABLED(FT_SECURITY) + /* + * Authenticate, returning the user if found + */ + virtual Authentication authenticate(const String& username, const String& password) = 0; + + /* + * Generate a JWT for the user provided + */ + virtual String generateJWT(User* user) = 0; + +#endif + + /* + * Check the request header for the Authorization token + */ + virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0; + + /** + * Filter a request with the provided predicate, only returning true if the predicate matches. + */ + virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided request to provide validation against an AuthenticationPredicate. + */ + virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided json request callback to provide validation against an AuthenticationPredicate. + */ + virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) = 0; +}; + +#endif // end SecurityManager_h diff --git a/lib/framework/SecuritySettingsService.cpp b/lib/framework/SecuritySettingsService.cpp new file mode 100644 index 0000000..87027db --- /dev/null +++ b/lib/framework/SecuritySettingsService.cpp @@ -0,0 +1,140 @@ +#include + +#if FT_ENABLED(FT_SECURITY) + +SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : + _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this), + _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE), + _jwtHandler(FACTORY_JWT_SECRET) { + addUpdateHandler([&](const String& originId) { configureJWTHandler(); }, false); +} + +void SecuritySettingsService::begin() { + _fsPersistence.readFromFS(); + configureJWTHandler(); +} + +Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { + AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); + if (authorizationHeader) { + String value = authorizationHeader->value(); + if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) { + value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); + return authenticateJWT(value); + } + } else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) { + AsyncWebParameter* tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER); + String value = tokenParamater->value(); + return authenticateJWT(value); + } + return Authentication(); +} + +void SecuritySettingsService::configureJWTHandler() { + _jwtHandler.setSecret(_state.jwtSecret); +} + +Authentication SecuritySettingsService::authenticateJWT(String& jwt) { + DynamicJsonDocument payloadDocument(MAX_JWT_SIZE); + _jwtHandler.parseJWT(jwt, payloadDocument); + if (payloadDocument.is()) { + JsonObject parsedPayload = payloadDocument.as(); + String username = parsedPayload["username"]; + for (User _user : _state.users) { + if (_user.username == username && validatePayload(parsedPayload, &_user)) { + return Authentication(_user); + } + } + } + return Authentication(); +} + +Authentication SecuritySettingsService::authenticate(const String& username, const String& password) { + for (User _user : _state.users) { + if (_user.username == username && _user.password == password) { + return Authentication(_user); + } + } + return Authentication(); +} + +inline void populateJWTPayload(JsonObject& payload, User* user) { + payload["username"] = user->username; + payload["admin"] = user->admin; +} + +boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) { + DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); + JsonObject payload = jsonDocument.to(); + populateJWTPayload(payload, user); + return payload == parsedPayload; +} + +String SecuritySettingsService::generateJWT(User* user) { + DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); + JsonObject payload = jsonDocument.to(); + populateJWTPayload(payload, user); + return _jwtHandler.buildJWT(payload); +} + +ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { + return [this, predicate](AsyncWebServerRequest* request) { + Authentication authentication = authenticateRequest(request); + return predicate(authentication); + }; +} + +ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) { + return [this, onRequest, predicate](AsyncWebServerRequest* request) { + Authentication authentication = authenticateRequest(request); + if (!predicate(authentication)) { + request->send(401); + return; + } + onRequest(request); + }; +} + +ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) { + return [this, onRequest, predicate](AsyncWebServerRequest* request, JsonVariant& json) { + Authentication authentication = authenticateRequest(request); + if (!predicate(authentication)) { + request->send(401); + return; + } + onRequest(request, json); + }; +} + +#else + +User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true); + +SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecurityManager() { +} +SecuritySettingsService::~SecuritySettingsService() { +} + +ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { + return [this, predicate](AsyncWebServerRequest* request) { return true; }; +} + +// Return the admin user on all request - disabling security features +Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) { + return Authentication(ADMIN_USER); +} + +// Return the function unwrapped +ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) { + return onRequest; +} + +ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, + AuthenticationPredicate predicate) { + return onRequest; +} + +#endif diff --git a/lib/framework/SecuritySettingsService.h b/lib/framework/SecuritySettingsService.h new file mode 100644 index 0000000..c1a3f17 --- /dev/null +++ b/lib/framework/SecuritySettingsService.h @@ -0,0 +1,119 @@ +#ifndef SecuritySettingsService_h +#define SecuritySettingsService_h + +#include +#include +#include +#include +#include + +#ifndef FACTORY_JWT_SECRET +#define FACTORY_JWT_SECRET "#{random}-#{random}" +#endif + +#ifndef FACTORY_ADMIN_USERNAME +#define FACTORY_ADMIN_USERNAME "admin" +#endif + +#ifndef FACTORY_ADMIN_PASSWORD +#define FACTORY_ADMIN_PASSWORD "admin" +#endif + +#ifndef FACTORY_GUEST_USERNAME +#define FACTORY_GUEST_USERNAME "guest" +#endif + +#ifndef FACTORY_GUEST_PASSWORD +#define FACTORY_GUEST_PASSWORD "guest" +#endif + +#define SECURITY_SETTINGS_FILE "/config/securitySettings.json" +#define SECURITY_SETTINGS_PATH "/rest/securitySettings" + +#if FT_ENABLED(FT_SECURITY) + +class SecuritySettings { + public: + String jwtSecret; + std::list users; + + static void read(SecuritySettings& settings, JsonObject& root) { + // secret + root["jwt_secret"] = settings.jwtSecret; + + // users + JsonArray users = root.createNestedArray("users"); + for (User user : settings.users) { + JsonObject userRoot = users.createNestedObject(); + userRoot["username"] = user.username; + userRoot["password"] = user.password; + userRoot["admin"] = user.admin; + } + } + + static StateUpdateResult update(JsonObject& root, SecuritySettings& settings) { + // secret + settings.jwtSecret = root["jwt_secret"] | SettingValue::format(FACTORY_JWT_SECRET); + + // users + settings.users.clear(); + if (root["users"].is()) { + for (JsonVariant user : root["users"].as()) { + settings.users.push_back(User(user["username"], user["password"], user["admin"])); + } + } else { + settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true)); + settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false)); + } + return StateUpdateResult::CHANGED; + } +}; + +class SecuritySettingsService : public StatefulService, public SecurityManager { + public: + SecuritySettingsService(AsyncWebServer* server, FS* fs); + + void begin(); + + // Functions to implement SecurityManager + Authentication authenticate(const String& username, const String& password); + Authentication authenticateRequest(AsyncWebServerRequest* request); + String generateJWT(User* user); + ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); + ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); + ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + ArduinoJsonJWT _jwtHandler; + + void configureJWTHandler(); + + /* + * Lookup the user by JWT + */ + Authentication authenticateJWT(String& jwt); + + /* + * Verify the payload is correct + */ + boolean validatePayload(JsonObject& parsedPayload, User* user); +}; + +#else + +class SecuritySettingsService : public SecurityManager { + public: + SecuritySettingsService(AsyncWebServer* server, FS* fs); + ~SecuritySettingsService(); + + // minimal set of functions to support framework with security settings disabled + Authentication authenticateRequest(AsyncWebServerRequest* request); + ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); + ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); + ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate); +}; + +#endif // end FT_ENABLED(FT_SECURITY) +#endif // end SecuritySettingsService_h diff --git a/lib/framework/SettingValue.cpp b/lib/framework/SettingValue.cpp new file mode 100644 index 0000000..c12e1b0 --- /dev/null +++ b/lib/framework/SettingValue.cpp @@ -0,0 +1,55 @@ +#include + +namespace SettingValue { + +#ifdef ESP32 +const String PLATFORM = "esp32"; +#elif defined(ESP8266) +const String PLATFORM = "esp8266"; +#endif + +/** + * Returns a new string after replacing each instance of the pattern with a value generated by calling the provided + * callback. + */ +String replaceEach(String value, String pattern, String (*generateReplacement)()) { + while (true) { + int index = value.indexOf(pattern); + if (index == -1) { + break; + } + value = value.substring(0, index) + generateReplacement() + value.substring(index + pattern.length()); + } + return value; +} + +/** + * Generates a random number, encoded as a hex string. + */ +String getRandom() { + return String(random(2147483647), HEX); +} + +/** + * Uses the station's MAC address to create a unique id for each device. + */ +String getUniqueId() { + uint8_t mac[6]; +#ifdef ESP32 + esp_read_mac(mac, ESP_MAC_WIFI_STA); +#elif defined(ESP8266) + wifi_get_macaddr(STATION_IF, mac); +#endif + char macStr[13] = {0}; + sprintf(macStr, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return String(macStr); +} + +String format(String value) { + value = replaceEach(value, "#{random}", getRandom); + value.replace("#{unique_id}", getUniqueId()); + value.replace("#{platform}", PLATFORM); + return value; +} + +}; // end namespace SettingValue diff --git a/lib/framework/SettingValue.h b/lib/framework/SettingValue.h new file mode 100644 index 0000000..cc5e0e3 --- /dev/null +++ b/lib/framework/SettingValue.h @@ -0,0 +1,14 @@ +#ifndef SettingValue_h +#define SettingValue_h + +#include + +#ifdef ESP8266 +#include +#endif + +namespace SettingValue { +String format(String value); +}; + +#endif // end SettingValue diff --git a/lib/framework/StatefulService.cpp b/lib/framework/StatefulService.cpp new file mode 100644 index 0000000..ece6b1f --- /dev/null +++ b/lib/framework/StatefulService.cpp @@ -0,0 +1,3 @@ +#include + +update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0; diff --git a/lib/framework/StatefulService.h b/lib/framework/StatefulService.h new file mode 100644 index 0000000..9a38e66 --- /dev/null +++ b/lib/framework/StatefulService.h @@ -0,0 +1,148 @@ +#ifndef StatefulService_h +#define StatefulService_h + +#include +#include + +#include +#include +#ifdef ESP32 +#include +#include +#endif + +#ifndef DEFAULT_BUFFER_SIZE +#define DEFAULT_BUFFER_SIZE 1024 +#endif + +enum class StateUpdateResult { + CHANGED = 0, // The update changed the state and propagation should take place if required + UNCHANGED, // The state was unchanged, propagation should not take place + ERROR // There was a problem updating the state, propagation should not take place +}; + +template +using JsonStateUpdater = std::function; + +template +using JsonStateReader = std::function; + +typedef size_t update_handler_id_t; +typedef std::function StateUpdateCallback; + +typedef struct StateUpdateHandlerInfo { + static update_handler_id_t currentUpdatedHandlerId; + update_handler_id_t _id; + StateUpdateCallback _cb; + bool _allowRemove; + StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : + _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; +} StateUpdateHandlerInfo_t; + +template +class StatefulService { + public: + template +#ifdef ESP32 + StatefulService(Args&&... args) : + _state(std::forward(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { + } +#else + StatefulService(Args&&... args) : _state(std::forward(args)...) { + } +#endif + + update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { + if (!cb) { + return 0; + } + StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); + _updateHandlers.push_back(updateHandler); + return updateHandler._id; + } + + void removeUpdateHandler(update_handler_id_t id) { + for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { + if ((*i)._allowRemove && (*i)._id == id) { + i = _updateHandlers.erase(i); + } else { + ++i; + } + } + } + + StateUpdateResult update(std::function stateUpdater, const String& originId) { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(std::function stateUpdater) { + beginTransaction(); + StateUpdateResult result = stateUpdater(_state); + endTransaction(); + return result; + } + + StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater stateUpdater, const String& originId) { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + if (result == StateUpdateResult::CHANGED) { + callUpdateHandlers(originId); + } + return result; + } + + StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater stateUpdater) { + beginTransaction(); + StateUpdateResult result = stateUpdater(jsonObject, _state); + endTransaction(); + return result; + } + + void read(std::function stateReader) { + beginTransaction(); + stateReader(_state); + endTransaction(); + } + + void read(JsonObject& jsonObject, JsonStateReader stateReader) { + beginTransaction(); + stateReader(_state, jsonObject); + endTransaction(); + } + + void callUpdateHandlers(const String& originId) { + for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) { + updateHandler._cb(originId); + } + } + + protected: + T _state; + + inline void beginTransaction() { +#ifdef ESP32 + xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); +#endif + } + + inline void endTransaction() { +#ifdef ESP32 + xSemaphoreGiveRecursive(_accessMutex); +#endif + } + + private: +#ifdef ESP32 + SemaphoreHandle_t _accessMutex; +#endif + std::list _updateHandlers; +}; + +#endif // end StatefulService_h diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp new file mode 100644 index 0000000..fa637e5 --- /dev/null +++ b/lib/framework/SystemStatus.cpp @@ -0,0 +1,45 @@ +#include + +SystemStatus::SystemStatus(AsyncWebServer* server, SecurityManager* securityManager) { + server->on(SYSTEM_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); +} + +void SystemStatus::systemStatus(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); + JsonObject root = response->getRoot(); +#ifdef ESP32 + root["esp_platform"] = "esp32"; + root["max_alloc_heap"] = ESP.getMaxAllocHeap(); + root["psram_size"] = ESP.getPsramSize(); + root["free_psram"] = ESP.getFreePsram(); +#elif defined(ESP8266) + root["esp_platform"] = "esp8266"; + root["max_alloc_heap"] = ESP.getMaxFreeBlockSize(); + root["heap_fragmentation"] = ESP.getHeapFragmentation(); +#endif + root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); + root["free_heap"] = ESP.getFreeHeap(); + root["sketch_size"] = ESP.getSketchSize(); + root["free_sketch_space"] = ESP.getFreeSketchSpace(); + root["sdk_version"] = ESP.getSdkVersion(); + root["flash_chip_size"] = ESP.getFlashChipSize(); + root["flash_chip_speed"] = ESP.getFlashChipSpeed(); + +// TODO - Ideally this class will take an *FS and extract the file system information from there. +// ESP8266 and ESP32 do not have feature parity in FS.h which currently makes that difficult. +#ifdef ESP32 + root["fs_total"] = ESPFS.totalBytes(); + root["fs_used"] = ESPFS.usedBytes(); +#elif defined(ESP8266) + FSInfo fs_info; + ESPFS.info(fs_info); + root["fs_total"] = fs_info.totalBytes; + root["fs_used"] = fs_info.usedBytes; +#endif + + response->setLength(); + request->send(response); +} diff --git a/lib/framework/SystemStatus.h b/lib/framework/SystemStatus.h new file mode 100644 index 0000000..3175ec4 --- /dev/null +++ b/lib/framework/SystemStatus.h @@ -0,0 +1,29 @@ +#ifndef SystemStatus_h +#define SystemStatus_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define MAX_ESP_STATUS_SIZE 1024 +#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" + +class SystemStatus { + public: + SystemStatus(AsyncWebServer* server, SecurityManager* securityManager); + + private: + void systemStatus(AsyncWebServerRequest* request); +}; + +#endif // end SystemStatus_h diff --git a/lib/framework/UploadFirmwareService.cpp b/lib/framework/UploadFirmwareService.cpp new file mode 100644 index 0000000..1858ace --- /dev/null +++ b/lib/framework/UploadFirmwareService.cpp @@ -0,0 +1,85 @@ +#include + +UploadFirmwareService::UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager) : + _securityManager(securityManager) { + server->on(UPLOAD_FIRMWARE_PATH, + HTTP_POST, + std::bind(&UploadFirmwareService::uploadComplete, this, std::placeholders::_1), + std::bind(&UploadFirmwareService::handleUpload, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); +#ifdef ESP8266 + Update.runAsync(true); +#endif +} + +void UploadFirmwareService::handleUpload(AsyncWebServerRequest* request, + const String& filename, + size_t index, + uint8_t* data, + size_t len, + bool final) { + if (!index) { + Authentication authentication = _securityManager->authenticateRequest(request); + if (AuthenticationPredicates::IS_ADMIN(authentication)) { + if (Update.begin(request->contentLength())) { + // success, let's make sure we end the update if the client hangs up + request->onDisconnect(UploadFirmwareService::handleEarlyDisconnect); + } else { + // failed to begin, send an error response + Update.printError(Serial); + handleError(request, 500); + } + } else { + // send the forbidden response + handleError(request, 403); + } + } + + // if we haven't delt with an error, continue with the update + if (!request->_tempObject) { + if (Update.write(data, len) != len) { + Update.printError(Serial); + handleError(request, 500); + } + if (final) { + if (!Update.end(true)) { + Update.printError(Serial); + handleError(request, 500); + } + } + } +} + +void UploadFirmwareService::uploadComplete(AsyncWebServerRequest* request) { + // if no error, send the success response + if (!request->_tempObject) { + request->onDisconnect(RestartService::restartNow); + AsyncWebServerResponse* response = request->beginResponse(200); + request->send(response); + } +} + +void UploadFirmwareService::handleError(AsyncWebServerRequest* request, int code) { + // if we have had an error already, do nothing + if (request->_tempObject) { + return; + } + // send the error code to the client and record the error code in the temp object + request->_tempObject = new int(code); + AsyncWebServerResponse* response = request->beginResponse(code); + request->send(response); +} + +void UploadFirmwareService::handleEarlyDisconnect() { +#ifdef ESP32 + Update.abort(); +#elif defined(ESP8266) + Update.end(); +#endif +} diff --git a/lib/framework/UploadFirmwareService.h b/lib/framework/UploadFirmwareService.h new file mode 100644 index 0000000..6312af1 --- /dev/null +++ b/lib/framework/UploadFirmwareService.h @@ -0,0 +1,38 @@ +#ifndef UploadFirmwareService_h +#define UploadFirmwareService_h + +#include + +#ifdef ESP32 +#include +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include + +#define UPLOAD_FIRMWARE_PATH "/rest/uploadFirmware" + +class UploadFirmwareService { + public: + UploadFirmwareService(AsyncWebServer* server, SecurityManager* securityManager); + + private: + SecurityManager* _securityManager; + void handleUpload(AsyncWebServerRequest* request, + const String& filename, + size_t index, + uint8_t* data, + size_t len, + bool final); + void uploadComplete(AsyncWebServerRequest* request); + void handleError(AsyncWebServerRequest* request, int code); + static void handleEarlyDisconnect(); +}; + +#endif // end UploadFirmwareService_h diff --git a/lib/framework/WebSocketTxRx.h b/lib/framework/WebSocketTxRx.h new file mode 100644 index 0000000..d2871ad --- /dev/null +++ b/lib/framework/WebSocketTxRx.h @@ -0,0 +1,273 @@ +#ifndef WebSocketTxRx_h +#define WebSocketTxRx_h + +#include +#include +#include + +#define WEB_SOCKET_CLIENT_ID_MSG_SIZE 128 + +#define WEB_SOCKET_ORIGIN "websocket" +#define WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX "websocket:" + +template +class WebSocketConnector { + protected: + StatefulService* _statefulService; + AsyncWebServer* _server; + AsyncWebSocket _webSocket; + size_t _bufferSize; + + WebSocketConnector(StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate, + size_t bufferSize) : + _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { + _webSocket.setFilter(securityManager->filterRequest(authenticationPredicate)); + _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); + _server->addHandler(&_webSocket); + _server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, std::placeholders::_1)); + } + + WebSocketConnector(StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + size_t bufferSize) : + _statefulService(statefulService), _server(server), _webSocket(webSocketPath), _bufferSize(bufferSize) { + _webSocket.onEvent(std::bind(&WebSocketConnector::onWSEvent, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6)); + _server->addHandler(&_webSocket); + } + + virtual void onWSEvent(AsyncWebSocket* server, + AsyncWebSocketClient* client, + AwsEventType type, + void* arg, + uint8_t* data, + size_t len) = 0; + + String clientId(AsyncWebSocketClient* client) { + return WEB_SOCKET_ORIGIN_CLIENT_ID_PREFIX + String(client->id()); + } + + private: + void forbidden(AsyncWebServerRequest* request) { + request->send(403); + } +}; + +template +class WebSocketTx : virtual public WebSocketConnector { + public: + WebSocketTx(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + _stateReader(stateReader) { + WebSocketConnector::_statefulService->addUpdateHandler( + [&](const String& originId) { transmitData(nullptr, originId); }, false); + } + + WebSocketTx(JsonStateReader stateReader, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateReader(stateReader) { + WebSocketConnector::_statefulService->addUpdateHandler( + [&](const String& originId) { transmitData(nullptr, originId); }, false); + } + + protected: + virtual void onWSEvent(AsyncWebSocket* server, + AsyncWebSocketClient* client, + AwsEventType type, + void* arg, + uint8_t* data, + size_t len) { + if (type == WS_EVT_CONNECT) { + // when a client connects, we transmit it's id and the current payload + transmitId(client); + transmitData(client, WEB_SOCKET_ORIGIN); + } + } + + private: + JsonStateReader _stateReader; + + void transmitId(AsyncWebSocketClient* client) { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WEB_SOCKET_CLIENT_ID_MSG_SIZE); + JsonObject root = jsonDocument.to(); + root["type"] = "id"; + root["id"] = WebSocketConnector::clientId(client); + size_t len = measureJson(jsonDocument); + AsyncWebSocketMessageBuffer* buffer = WebSocketConnector::_webSocket.makeBuffer(len); + if (buffer) { + serializeJson(jsonDocument, (char*)buffer->get(), len + 1); + client->text(buffer); + } + } + + /** + * Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if + * specified. + * + * Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach + * simplifies the client and the server implementation but may not be sufficent for all use-cases. + */ + void transmitData(AsyncWebSocketClient* client, const String& originId) { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); + JsonObject root = jsonDocument.to(); + root["type"] = "payload"; + root["origin_id"] = originId; + JsonObject payload = root.createNestedObject("payload"); + WebSocketConnector::_statefulService->read(payload, _stateReader); + + size_t len = measureJson(jsonDocument); + AsyncWebSocketMessageBuffer* buffer = WebSocketConnector::_webSocket.makeBuffer(len); + if (buffer) { + serializeJson(jsonDocument, (char*)buffer->get(), len + 1); + if (client) { + client->text(buffer); + } else { + WebSocketConnector::_webSocket.textAll(buffer); + } + } + } +}; + +template +class WebSocketRx : virtual public WebSocketConnector { + public: + WebSocketRx(JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + _stateUpdater(stateUpdater) { + } + + WebSocketRx(JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, server, webSocketPath, bufferSize), _stateUpdater(stateUpdater) { + } + + protected: + virtual void onWSEvent(AsyncWebSocket* server, + AsyncWebSocketClient* client, + AwsEventType type, + void* arg, + uint8_t* data, + size_t len) { + if (type == WS_EVT_DATA) { + AwsFrameInfo* info = (AwsFrameInfo*)arg; + if (info->final && info->index == 0 && info->len == len) { + if (info->opcode == WS_TEXT) { + DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); + DeserializationError error = deserializeJson(jsonDocument, (char*)data); + if (!error && jsonDocument.is()) { + JsonObject jsonObject = jsonDocument.as(); + WebSocketConnector::_statefulService->update( + jsonObject, _stateUpdater, WebSocketConnector::clientId(client)); + } + } + } + } + } + + private: + JsonStateUpdater _stateUpdater; +}; + +template +class WebSocketTxRx : public WebSocketTx, public WebSocketRx { + public: + WebSocketTxRx(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + SecurityManager* securityManager, + AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + WebSocketTx(stateReader, + statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize), + WebSocketRx(stateUpdater, + statefulService, + server, + webSocketPath, + securityManager, + authenticationPredicate, + bufferSize) { + } + + WebSocketTxRx(JsonStateReader stateReader, + JsonStateUpdater stateUpdater, + StatefulService* statefulService, + AsyncWebServer* server, + const char* webSocketPath, + size_t bufferSize = DEFAULT_BUFFER_SIZE) : + WebSocketConnector(statefulService, server, webSocketPath, bufferSize), + WebSocketTx(stateReader, statefulService, server, webSocketPath, bufferSize), + WebSocketRx(stateUpdater, statefulService, server, webSocketPath, bufferSize) { + } + + protected: + void onWSEvent(AsyncWebSocket* server, + AsyncWebSocketClient* client, + AwsEventType type, + void* arg, + uint8_t* data, + size_t len) { + WebSocketRx::onWSEvent(server, client, type, arg, data, len); + WebSocketTx::onWSEvent(server, client, type, arg, data, len); + } +}; + +#endif diff --git a/lib/framework/WiFiScanner.cpp b/lib/framework/WiFiScanner.cpp new file mode 100644 index 0000000..a6f2b65 --- /dev/null +++ b/lib/framework/WiFiScanner.cpp @@ -0,0 +1,70 @@ +#include + +WiFiScanner::WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager) { + server->on(SCAN_NETWORKS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); + server->on(LIST_NETWORKS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1), + AuthenticationPredicates::IS_ADMIN)); +}; + +void WiFiScanner::scanNetworks(AsyncWebServerRequest* request) { + if (WiFi.scanComplete() != -1) { + WiFi.scanDelete(); + WiFi.scanNetworks(true); + } + request->send(202); +} + +void WiFiScanner::listNetworks(AsyncWebServerRequest* request) { + int numNetworks = WiFi.scanComplete(); + if (numNetworks > -1) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE); + JsonObject root = response->getRoot(); + JsonArray networks = root.createNestedArray("networks"); + for (int i = 0; i < numNetworks; i++) { + JsonObject network = networks.createNestedObject(); + network["rssi"] = WiFi.RSSI(i); + network["ssid"] = WiFi.SSID(i); + network["bssid"] = WiFi.BSSIDstr(i); + network["channel"] = WiFi.channel(i); +#ifdef ESP32 + network["encryption_type"] = (uint8_t)WiFi.encryptionType(i); +#elif defined(ESP8266) + network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i)); +#endif + } + response->setLength(); + request->send(response); + } else if (numNetworks == -1) { + request->send(202); + } else { + scanNetworks(request); + } +} + +#ifdef ESP8266 +/* + * Convert encryption type to standard used by ESP32 rather than the translated form which the esp8266 libaries expose. + * + * This allows us to use a single set of mappings in the UI. + */ +uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) { + switch (encryptionType) { + case ENC_TYPE_NONE: + return AUTH_OPEN; + case ENC_TYPE_WEP: + return AUTH_WEP; + case ENC_TYPE_TKIP: + return AUTH_WPA_PSK; + case ENC_TYPE_CCMP: + return AUTH_WPA2_PSK; + case ENC_TYPE_AUTO: + return AUTH_WPA_WPA2_PSK; + } + return -1; +} +#endif diff --git a/lib/framework/WiFiScanner.h b/lib/framework/WiFiScanner.h new file mode 100644 index 0000000..6f91030 --- /dev/null +++ b/lib/framework/WiFiScanner.h @@ -0,0 +1,35 @@ +#ifndef WiFiScanner_h +#define WiFiScanner_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include + +#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks" +#define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks" + +#define MAX_WIFI_SCANNER_SIZE 1024 + +class WiFiScanner { + public: + WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager); + + private: + void scanNetworks(AsyncWebServerRequest* request); + void listNetworks(AsyncWebServerRequest* request); + +#ifdef ESP8266 + uint8_t convertEncryptionType(uint8_t encryptionType); +#endif +}; + +#endif // end WiFiScanner_h diff --git a/lib/framework/WiFiSettingsService.cpp b/lib/framework/WiFiSettingsService.cpp new file mode 100644 index 0000000..1c228c1 --- /dev/null +++ b/lib/framework/WiFiSettingsService.cpp @@ -0,0 +1,100 @@ +#include + +WiFiSettingsService::WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager), + _fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), + _lastConnectionAttempt(0) { + // We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default. + // If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future. + if (WiFi.getMode() != WIFI_OFF) { + WiFi.mode(WIFI_OFF); + } + + // Disable WiFi config persistance and auto reconnect + WiFi.persistent(false); + WiFi.setAutoReconnect(false); +#ifdef ESP32 + // Init the wifi driver on ESP32 + WiFi.mode(WIFI_MODE_MAX); + WiFi.mode(WIFI_MODE_NULL); + WiFi.onEvent( + std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); + WiFi.onEvent(std::bind(&WiFiSettingsService::onStationModeStop, this, std::placeholders::_1, std::placeholders::_2), + WiFiEvent_t::SYSTEM_EVENT_STA_STOP); +#elif defined(ESP8266) + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected( + std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); +#endif + + addUpdateHandler([&](const String& originId) { reconfigureWiFiConnection(); }, false); +} + +void WiFiSettingsService::begin() { + _fsPersistence.readFromFS(); + reconfigureWiFiConnection(); +} + +void WiFiSettingsService::reconfigureWiFiConnection() { + // reset last connection attempt to force loop to reconnect immediately + _lastConnectionAttempt = 0; + +// disconnect and de-configure wifi +#ifdef ESP32 + if (WiFi.disconnect(true)) { + _stopping = true; + } +#elif defined(ESP8266) + WiFi.disconnect(true); +#endif +} + +void WiFiSettingsService::loop() { + unsigned long currentMillis = millis(); + if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { + _lastConnectionAttempt = currentMillis; + manageSTA(); + } +} + +void WiFiSettingsService::manageSTA() { + // Abort if already connected, or if we have no SSID + if (WiFi.isConnected() || _state.ssid.length() == 0) { + return; + } + // Connect or reconnect as required + if ((WiFi.getMode() & WIFI_STA) == 0) { + Serial.println(F("Connecting to WiFi.")); + if (_state.staticIPConfig) { + // configure for static IP + WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); + } else { + // configure for DHCP +#ifdef ESP32 + WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); + WiFi.setHostname(_state.hostname.c_str()); +#elif defined(ESP8266) + WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY); + WiFi.hostname(_state.hostname); +#endif + } + // attempt to connect to the network + WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); + } +} + +#ifdef ESP32 +void WiFiSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { + WiFi.disconnect(true); +} +void WiFiSettingsService::onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info) { + if (_stopping) { + _lastConnectionAttempt = 0; + _stopping = false; + } +} +#elif defined(ESP8266) +void WiFiSettingsService::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { + WiFi.disconnect(true); +} +#endif diff --git a/lib/framework/WiFiSettingsService.h b/lib/framework/WiFiSettingsService.h new file mode 100644 index 0000000..3d1dc22 --- /dev/null +++ b/lib/framework/WiFiSettingsService.h @@ -0,0 +1,112 @@ +#ifndef WiFiSettingsService_h +#define WiFiSettingsService_h + +#include +#include +#include +#include +#include + +#ifndef FACTORY_WIFI_SSID +#define FACTORY_WIFI_SSID "" +#endif + +#ifndef FACTORY_WIFI_PASSWORD +#define FACTORY_WIFI_PASSWORD "" +#endif + +#ifndef FACTORY_WIFI_HOSTNAME +#define FACTORY_WIFI_HOSTNAME "#{platform}-#{unique_id}" +#endif + +#define WIFI_SETTINGS_FILE "/config/wifiSettings.json" +#define WIFI_SETTINGS_SERVICE_PATH "/rest/wifiSettings" + +#define WIFI_RECONNECTION_DELAY 1000 * 30 + +class WiFiSettings { + public: + // core wifi configuration + String ssid; + String password; + String hostname; + bool staticIPConfig; + + // optional configuration for static IP address + IPAddress localIP; + IPAddress gatewayIP; + IPAddress subnetMask; + IPAddress dnsIP1; + IPAddress dnsIP2; + + static void read(WiFiSettings& settings, JsonObject& root) { + // connection settings + root["ssid"] = settings.ssid; + root["password"] = settings.password; + root["hostname"] = settings.hostname; + root["static_ip_config"] = settings.staticIPConfig; + + // extended settings + JsonUtils::writeIP(root, "local_ip", settings.localIP); + JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP); + JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask); + JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1); + JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2); + } + + static StateUpdateResult update(JsonObject& root, WiFiSettings& settings) { + settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; + settings.password = root["password"] | FACTORY_WIFI_PASSWORD; + settings.hostname = root["hostname"] | SettingValue::format(FACTORY_WIFI_HOSTNAME); + settings.staticIPConfig = root["static_ip_config"] | false; + + // extended settings + JsonUtils::readIP(root, "local_ip", settings.localIP); + JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP); + JsonUtils::readIP(root, "subnet_mask", settings.subnetMask); + JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1); + JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2); + + // Swap around the dns servers if 2 is populated but 1 is not + if (settings.dnsIP1 == INADDR_NONE && settings.dnsIP2 != INADDR_NONE) { + settings.dnsIP1 = settings.dnsIP2; + settings.dnsIP2 = INADDR_NONE; + } + + // Turning off static ip config if we don't meet the minimum requirements + // of ipAddress, gateway and subnet. This may change to static ip only + // as sensible defaults can be assumed for gateway and subnet + if (settings.staticIPConfig && + (settings.localIP == INADDR_NONE || settings.gatewayIP == INADDR_NONE || settings.subnetMask == INADDR_NONE)) { + settings.staticIPConfig = false; + } + return StateUpdateResult::CHANGED; + } +}; + +class WiFiSettingsService : public StatefulService { + public: + WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + + void begin(); + void loop(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + unsigned long _lastConnectionAttempt; + +#ifdef ESP32 + bool _stopping; + void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + void onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ESP8266) + WiFiEventHandler _onStationModeDisconnectedHandler; + void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); +#endif + + void reconfigureWiFiConnection(); + void manageSTA(); +}; + +#endif // end WiFiSettingsService_h diff --git a/lib/framework/WiFiStatus.cpp b/lib/framework/WiFiStatus.cpp new file mode 100644 index 0000000..de69542 --- /dev/null +++ b/lib/framework/WiFiStatus.cpp @@ -0,0 +1,75 @@ +#include + +WiFiStatus::WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager) { + server->on(WIFI_STATUS_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); +#ifdef ESP32 + WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED); + WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); + WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); +#elif defined(ESP8266) + _onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected); + _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected); + _onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP); +#endif +} + +#ifdef ESP32 +void WiFiStatus::onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.println(F("WiFi Connected.")); +} + +void WiFiStatus::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.print(F("WiFi Disconnected. Reason code=")); + Serial.println(info.disconnected.reason); +} + +void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.printf_P( + PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), WiFi.localIP().toString().c_str(), WiFi.getHostname()); +} +#elif defined(ESP8266) +void WiFiStatus::onStationModeConnected(const WiFiEventStationModeConnected& event) { + Serial.print(F("WiFi Connected. SSID=")); + Serial.println(event.ssid); +} + +void WiFiStatus::onStationModeDisconnected(const WiFiEventStationModeDisconnected& event) { + Serial.print(F("WiFi Disconnected. Reason code=")); + Serial.println(event.reason); +} + +void WiFiStatus::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { + Serial.printf_P( + PSTR("WiFi Got IP. localIP=%s, hostName=%s\r\n"), event.ip.toString().c_str(), WiFi.hostname().c_str()); +} +#endif + +void WiFiStatus::wifiStatus(AsyncWebServerRequest* request) { + AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE); + JsonObject root = response->getRoot(); + wl_status_t status = WiFi.status(); + root["status"] = (uint8_t)status; + if (status == WL_CONNECTED) { + root["local_ip"] = WiFi.localIP().toString(); + root["mac_address"] = WiFi.macAddress(); + root["rssi"] = WiFi.RSSI(); + root["ssid"] = WiFi.SSID(); + root["bssid"] = WiFi.BSSIDstr(); + root["channel"] = WiFi.channel(); + root["subnet_mask"] = WiFi.subnetMask().toString(); + root["gateway_ip"] = WiFi.gatewayIP().toString(); + IPAddress dnsIP1 = WiFi.dnsIP(0); + IPAddress dnsIP2 = WiFi.dnsIP(1); + if (dnsIP1 != INADDR_NONE) { + root["dns_ip_1"] = dnsIP1.toString(); + } + if (dnsIP2 != INADDR_NONE) { + root["dns_ip_2"] = dnsIP2.toString(); + } + } + response->setLength(); + request->send(response); +} diff --git a/lib/framework/WiFiStatus.h b/lib/framework/WiFiStatus.h new file mode 100644 index 0000000..197bd64 --- /dev/null +++ b/lib/framework/WiFiStatus.h @@ -0,0 +1,45 @@ +#ifndef WiFiStatus_h +#define WiFiStatus_h + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define MAX_WIFI_STATUS_SIZE 1024 +#define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus" + +class WiFiStatus { + public: + WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager); + + private: +#ifdef ESP32 + // static functions for logging WiFi events to the UART + static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info); + static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); + static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ESP8266) + // handler refrences for logging important WiFi events over serial + WiFiEventHandler _onStationModeConnectedHandler; + WiFiEventHandler _onStationModeDisconnectedHandler; + WiFiEventHandler _onStationModeGotIPHandler; + // static functions for logging WiFi events to the UART + static void onStationModeConnected(const WiFiEventStationModeConnected& event); + static void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); + static void onStationModeGotIP(const WiFiEventStationModeGotIP& event); +#endif + + void wifiStatus(AsyncWebServerRequest* request); +}; + +#endif // end WiFiStatus_h diff --git a/lib/readme.txt b/lib/readme.txt new file mode 100644 index 0000000..dbadc3d --- /dev/null +++ b/lib/readme.txt @@ -0,0 +1,36 @@ + +This directory is intended for the project specific (private) libraries. +PlatformIO will compile them to static libraries and link to executable file. + +The source code of each library should be placed in separate directory, like +"lib/private_lib/[here are source files]". + +For example, see how can be organized `Foo` and `Bar` libraries: + +|--lib +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| |--Foo +| | |- Foo.c +| | |- Foo.h +| |- readme.txt --> THIS FILE +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- http://docs.platformio.org/page/librarymanager/ldf.html diff --git a/media/build.png b/media/build.png new file mode 100644 index 0000000000000000000000000000000000000000..33e807a93add396239ae3d22f59ffe568250b309 GIT binary patch literal 8646 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 literal 0 HcmV?d00001 diff --git a/media/devserver.png b/media/devserver.png new file mode 100644 index 0000000000000000000000000000000000000000..003e4e0d6029ef78376eda327877062753e3cddd GIT binary patch literal 73249 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 E#(qm4RXZ}{RcCHLb?%@4t@m{l*TV|; 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Ν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 z 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 B0tx^C literal 0 HcmV?d00001 diff --git a/media/esp12e.jpg b/media/esp12e.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b9b58ca6804aca5f02d4665c510a4ba3259daf2 GIT binary patch literal 17509 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 SrH}srhmZ4rD(Wy?ng7{xPy!DC literal 0 HcmV?d00001 diff --git a/media/esp32.jpg b/media/esp32.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f1da1222488a40c7a8a848412f4aeda5bcbaad40 GIT binary patch literal 26212 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�%d0c>oV_YpXmkd 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 daismMx4|Jfa=Jf;8u literal 0 HcmV?d00001 diff --git a/media/framework.png b/media/framework.png new file mode 100644 index 0000000000000000000000000000000000000000..e157dda6d65320dd1b10ae6e35b0c85537dbb44e GIT binary patch literal 58594 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

oHGZ6>iJeRH8|2!=IYW!Q01%mJr*#5A{ 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 pw*tSYh0oAKlLNT=e?8zW2UjCWQIii> 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 literal 0 HcmV?d00001 diff --git a/media/uploadfs.png b/media/uploadfs.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5ee688d8f4102d58cc8a61916845adfb5e695b GIT binary patch literal 8644 zcmai)dpy(c|Nj+bQf7KPB!@zIha8gRFlVWcO6AzFH9BCG!<=W6oX>fChZIXHavEhh zZkTguyxB(1hlpY1Fl^>GdVk)(&-eEE9sbyD+v|1hx~|vrc|D)k{rR{Sd*iyrkwcP) zczAe@T)Av^i-%`Focs9?z`@-s52Y#Gw|&T47A8C;U!`WbU-%vvUo+<6DN7b)!w+!3 z3wT|=jpX4GYTSM8YxT^(%foZ>_7yW@n?UG77OXt$9(rcd9N))d%Q?kPdBevxZSbIc zxQ6$o{)SK%JfifpH^tEQ+wDurpIYW5gbOYlR+mJ;jf=0i>u}C=U$}eb^AWRiMP3a> zRhc7xCHK1)oW8EkJu=W+xkj7`$Yu_+=07n9+S{2EsZ4bC43jyaII~$2z(9U|uT7Y} zHvHTkI`C-l^TO};5yB>blMydIK6;DqcJd7BaI7vYsrG1kuT5M|%x$LCan_no?zJg; z?kb6JjGhi&c{v*56!WNhB2KwlyLSA@@Vj(qHR;T+Mn}$zK?J+LTQy$D=~S(m|G4;v zrK-Cw=|IJh?Cvu0PzspDZk4K7^c!}_h5YedJCMIs`3Z!la zx3lNPu}69x|9Bawzg|rvlnhq;A$mb_q`iK&j)}nCRLaFN5ibCfjV}792S47k{O`LR zq5t|>38)!+Dwvrh3=-S@X*c;9z|{3h#Afu0{I9;Km(3h@2PW;$_*caCmLJVed(;OR zFw7dE`9{Q)?imR4UIZ>nn0>$a3CkL~dD8zs#(MFuh+^hoO2tSpg4yqy?592CWr{>o zpGAjEJu1d20>P^{0?1(IdD`yiUUTQ318TOD^s3cH17OT{Jwu9bgaIh3G>E<2TJ!GR zJ5WqQTGl5!kpD1u9za93R|RtB>vV9Up)%-#DnsF_5~<{RD=%lEp~K zezshGRW3hHp(QvoiV&}vybrcP5>HDkykT7Hc0^cT1P(C!{sGh9p0{=$M?D#elXiLy zvRDy;s*-FFqJGeL9L*`t`eq$$^NW4bpum8zB`ymJTz}v+ziYH|u4S&V{1x~wy1YFF zDWBeNLh4|glE`QjLJ3F-$R>NwWCfJOI4^G`h~}x4%-kJfmKQu1AD_S;*^xdHFeH``~yDKvA5iR`WM$HS4*E5cYq*rSG@vPR-1s^N^+Gx zKai#2X17F@JI7m}OAt>-@c{xuTAZAJ?DK9Pj?;QH0?}j6!GxI{76W!VA7ZmpZS~Qe zGB6kL3b3**Nqexn6zsTOdP%|M8wp>&==FqNd^S4Ba20fr5|wwZ-)8(oOKgOaQj}S& z3q{h2C)N(?BA-8%rwA|lpq<<-qd^m~64DvM;l)>bgY^RQVTr>Pi{dw6qns$NRp6(r zV@q#uf3y?w1usnSFpf%MepEC?-XV&H>)RgIv9&oID}uX?K=;*Zsk&01+Xu=zZ5E07 zqtT|85fs9Y!O0wq*8Q_`D%?29kmh#fKAJT3Y&|@)(FuEM!ia(D=mMgd?gPsAyoK<&&emhvRd=Zfo@AsY34s-d~YLDJzk? z8eKIK*E<*kN%!gw9?c=c)s8Ra>B%%sc35cKQ;TV+=Htc?64j$<)C!7TO^`VsK?@$&q`gi!n0CxRq z;=lvi{f!Ag@WkG80^s{PZ6hLo#UVuU5LMw<+>(6FeqB|00tNX~Z$I$EVb%(3G{0WV zot4ugesOU%||w#oCpx^sk0H z?8irWPm}dmI^VnQHZaMMIiJQSF@fkvJ3CA*cC5LUUq&OY<8v+Uo^9Qmk2-iS3kmMq z&0_xI&b~J*1k79ZC$~d`x>&ykIc&_$cim3<`%IrWhb%V_pEf89{i-wb_uM=8?Pbhn zYjr*e!?VYJ*+$*sAKTyngB6}IrUiERZ@a1UC&iX749l|HiHr4Nyu;&-(KYEXj&b&$ zEyh>xt*Ajqw;Pe<`of-A@+zQ{JIywIK zfb(Txn8M_cd+>OoSj;9Bx6N35HOjt)L~GSDC$Y1t!3+xvCT)0Kwptt(;@yNB4W_vz z;h94pkuS)Qi>jn`bTO+_#Y}=wO}*hS$waJaeaL9~$~00B zpxHBvGH`q5`r%5qU?%W=J;u;KEpuH96cuPG)=rAv-UkCZd7>!EGhF;xpsvIl0Ff zYx%ge%7VSYGQ>gfPawGX&fK+DbEfy}`MsTr1YFax5~5y`{Gz(B-Jrv74X^3sx2-mm)OOa>BqaCYZKzamv%j zG?USI>sLxpZm<*y3Hb{Jifas6{#hyU7vUjDKzwJ=6DNGN=}(7SAgM5+>~xg4brSPSMqoOFU31Sntc;OTZzY#dTkwHQY%Uzx z;w&!6ZrLy&ScYneKtf~^Hk0Rvw4S2Sm*$rlhTxCQt*%!a2OiYnSy|4ZeK}Q0JHAsu z|E|R=5^s1kPI}*Lex8q^BwZA_BqAPn#;MOHbfWsQq$EnU&R!*2su&|h{gC$+ML2tW zZP)g7>CVG?7HjzRhiz)yJag9uH2%iuf{2dlWM4-vgMlv+VCME0W&_rx)NS7ka8uHP_-a1;$b%MuIS?u;deHViOo;R?j_)109I zhQs}pK91}K*W#$>R;1~rh7iLIXDM69P^C%#F_(NR{mWinuh z*v|}C+{?w(9>jG$La2;}wiJWocUKwS+MLgxaA5ib!0E=tPJ7fH;;CD|SrXB52|D6P z5xY3~C^HFHiegHmz7B%WnYE1zC=qrV!d;4>Y%gK{v|pRZ+oop6q@e}*o(rn*(Jq3b zyb#ThBS;Y3lwZ%qc-MyGH@5F88j8zO>??&oaGJ6%Mc`cosL_0xuTM|hteEzwYf?<6 z8gB!TAVxw&%2s_d#PXG~ChOZ^?efgAg0Xj+G7~a$kSqRdofi$Ol#GD3JYGgA09MV zZ9hhkt~vp-JHSJugq%*mdt(KVJJkl~50aZRL$eP%$k$DK=v#bNeX@jvxAbHATnaf) z`XRa6Ouv*Fv}K^XzUnS2PUjD2m|~4?tV>ntPu*DKtE2qP4l9c&VbVAEScM3I{pOx|RqFNEJ+YgPZbhznK?1Mr zLii%t`r=62rZ(bN9hq>+z=F4m@FtsUwqwH9i0%S`EsXB1--Yn@Fv>_~E0@1qT}m-M zXOk~1urBIg%$p_69WsO>MD~Bc@6vVA0lpZ%TW-j;wE$nT^j7=jyA&98i^l#)^^{`W zE9S=<_HDD*9%)oT=_BOdr-fUsK!KkR+&!ppwhdTpr2CuURjX9bPat;>2Ra$0*2FVHO=9#{Wrb8_#4p={0;b}Jf}tAgP)y2dkApPNBq01(nY!Xc17&G40^R^`rE~&NuZS>x1<`z(>Rdc`?(F6eauxH z>kz9fbcgeJ=4)2h+Aa;ea$1cmbr01Q(Rgy%L`6VcC^fFsqr$Z{akwFzpJsmdS-ec8 zM+t@Y*5&62is3xXBnhgi#DmZ5;SQ+!J1}2O-G)FU zH;;PKN@^^u{>nh2n!R?ENVWH){lHngi{B@0KrI;?s}SVA&;36}SZfzYc18%SU*KUK z6B(yIedB?=QWSwLp^)esO@x)IH@XZdiLhHO`XS#%K^tlQR~%M@q9UVxA`hIv`osUe9Jrk zid9j4v$Zk?ChK&pVhhI--}PJS%c>Z4zNhg*;)1G51;vK#ALPC?{|h@-4>h2KUiPJw z&Fr5>s2a;Ka0lr9?nCL8beUe(*q2I5=pIaZd1x>d;i9pJ1k_^_=Vm5&or-K=J&Mr! zNOEP_j7PIb5m4a#P4f&U1Zn(hTx@yT-lU~eu5zaMs!hN8ylE7)JLTmozWpGpc^!FJLnE|H{Gt zwNU?A^Zx+jf5LQU7F~6_YH`w{u4;98+!9J#Y8XVoe|Eo)ix8V9p7o$>_no$H{)FV*qt zJK=qW=Aor84NJOY4|xteG~37>V|y>%-tk`a4t+?7!zf#iPJhc146 z?;NjGDWlIEx+m9RJheUb01E!npyi?*A$)R20OHQ3 zo@eGhhKb2}Wjr(jyLYWr;^v*Y z`~C*S&jAdgZG0(2uqwn;KLA0lualVzDYjp3FT{yL3u_oe)ZIcSBL7J4%s3Qw6tYrT zoLP^ef>C)=eMbz^)C@RyIwJd}vPmD}2i+AA(Pv1Uvz6~@O8uZzKWAM+1a_^Y$zApx z#`KtXOmG*fgPx!$T(`EeH9I_?;Wbn5bMuMpy5!`H9AKyVD?C3#cPvC@-&ob_Lnkc! zsB9tn+$j9|c40OZ(bCHp5muHDftp#ipz3? zf3sg9Dmvj6zsT#2uN2EfyCfWs&)JsM_IK!|iTHV2bwTWlC%1k~CMR=3&2JDwT8@R+ ziIK2A%jGJb%G_<(B_kftJFdi5Uk)$%wq^Yy_{7$}D43=G9X0G+^4Vy4&SXor;Fgf> ziDV}2sV;;+pY{1+UoveBU-c;QJBwV2e}HBI&_T6gEmO9Mw&li!o)$3PkI1rd@Fwlh z<`j-iX@MtcRTLA8%a*bcJE;k1SmlGStlu(^&owS&sbNDg)?O5tU<&YSv@of?aR?h> z>)Sp1dfAk^Jr~y25DBKuDKGUnttqPhnoFUmW$7aSW7$44{W+@BLi)y3k8Zv!x%bO# znTJ$M_qR_uTenxhOF_(@k<|gKr>t9w8iw1mSzUp6;CI?L1sEzd>44Vi!cKQ3{(UwC zc691}lsCG3V|=W=kBcMAo$_X5D15MDiF_q& zwkB#-i;+4{-Cj?8FUVU+@W`#GL7gY^g8&x<-!wM+)17 zFZhXsXApsl+H`Ev+k+Gw8(oH0qeI6UUxgr?e5#8>v(#t6Poau9zd!aoHrdi-ExkI1 zD)@Fgm$TKIf=1hH2(;B*WIeD4lpU(m7uC41Xm7V0hO+lnNhDt{2iCq2-SCUXQrCa} z<|f}jGqU8nzMu3Z)D-=FhcF@9(d59n;}1BglFgM!78vNVK@rgB$7T^(PtZS5bDwS+ z2X6J&-EOopveRf$b9ywum8cwKgU$Q3c9xeHooS31fTJQ8)oQ_L-FaTmD{|GRgKE6f zLO4&)=3+Dc6z*W~ad(vF9%Yg$YvU%!pT!86vEc3^?1T9Oh%Fp?};qnqp_vhJ8a6H=mU>BTqnJ`W0*1Ovv(KJo4O|<{l9pP zq~(c1F^7=k#68V}>Kp-H>+2nM4c%JUqc`n%L)N5hz|J(fuBTi_G)Hay-5#z+tc|~a z={bwp1T& zsM7icmqK@t0cStGKv`6SSVW}~S1N)hoqs*C5gE;9uWk@A$ zff2;;PSN7K(Er(`D6J}9eWMW8<%=Ucb7f%@Hq)xRt|=9gv)bf{p( zAR>#vNMy${zoQt(!B?zGW{$-EGp0j88Wuyd(WjQ}Ah8F_Z*O``ewP-$OXW4pvI8Y0n2hWj92pW??@kOzR zgoGtk6PA!+xAug9us0b_&TU?lR3-8O5lFwS&4VsA1trP@K{nn{OBPd5=vlWkK0<$> z|EDH*RRdZBJ18_?TIiDL@KGz3#0W7gzbj`nbgNgslJ=m9$lPWmKU`jx=v5Ek7H-C( z_HVY1#FiSs!>wR4|Fa%9TPQrh5rJcX=uNOHLUri%`rZM=g;#k1m1V)Yjii)~zcN?> zoI)7n{z?Ulsm(2-xyWf2K5~v%+)`Djpt$aogLy=J3b%7YNP*$!-nddFFR7o0!QQu$ zO)vCak>Z{YY!X)&>Xy(KM{t79-AQC;7A{K-*qx+q z&0e0^FaXTQ;DRa&5k^;W+Pju*)L$Cg08XzoK0Q0??vMeP%+AvdMjQc!EqSMeEv0nt z7r$gAwb^kU@#!~sJy_>YAp43_qJ2+lQJSG&wb2TCU-8<4xXv*oPNHd*v#V@&lcP37 zMfYMhPm-}y&tGjCEtF_xw|%deEJfH+gBh-OK(L{l&Bg7ywJ`2-7x^V_di{CafBwhj zC-YVV`4-8#+oNcz$uDR%2{ZiGm0EUWjlXtJI{o`H$>bkln42gb4VLVh$^@XP@Naea z|4+sB)$yf@*pQE?)gFtNh{^h7L+JMWC$c{c%0gUwSsG0JnOPKxpK2DbRE%15EFJms ziF@j@7%2{waHfCrsA7(w_z}X0+0DCQJ17>Dfo&&73X^6#JIy6VUxTj1oEnereH(oP zKUg-|vN+ZdPXC#Rby}^rMHUlJgRknm9%i(q$ZIZy>*^a^l>;;d3-B(4Z((hrE{E4T zwrW}N07v)S9ktYXOKuIB)M*~EsqKc=q&8^Ip0rtj{3-;4Ty}}{icm(dk>=@b$9!=Ud<6+95ff>j?x`{leNNiC}36OA{eS`j&utWC9rXy}u}TnEm9Z>E($_R-PH@ zl|54QngE6j80@XbuJR}`aF+WI?hO8;Wlx~APLD4ohNte8iH>jbWV(qKKdqhnUBU^$ zi0}Rmo&$Z=RrXhL(JTUrDLoo6Dp=B|G~O4CCUB{8U1d-&?-Xh4CzWQjPBffLRCXNz zg*!AU9MplZ!n2^7W7%8Z=^D!YbVRx`=CNTHG?{+ewORTb;^&I)ndJG9BHF$X^6SNP zrs+0EYYa8M-9}~sxz)ha-HOz&{tj8&XR}JWkN1$_yjIJe*qFs@qT!dn;lQ6dvwtGk zFlnx6Q<@+?IKRC%0b$ygzNZ~-E8aS9XAtc-Uw+z=(mjX#gAkRIZU2eZuarDj z>`HmN^*nFgp5@-0R?PVDkTA_%m5LcPfWpzS&8Q+z&h*@0VXoJ|Aq$CV zgnX9z`4WVB9}u}Ec_-U%ArhzYxLJs6%mi+#Bc8GRz0!e)cycAN>K@$+FeJpT|1kRF z!1~V*oc_6bY3+5V8$iQo{SlZT6>Z4wF7Wg6%Q+P#bdE82QVml~K4hqf-T3iyqZxh4obwhIyBKE!9qKqnzy_`?_`{oD~D_`@Rv(dIyt6>$8}ZilnuwX*-B1n&(&AUUTjJU1t;hOg~`odyCpnScXfv`eyhZpJG1~{ zJlhwblpLQeMg+d``~8+_M^w$!f^<9(4pjn(g0w>wFh~a#(4zuU1yo9?A|NFJCG;*wKu{3%2+{&V z=p-7NNL5HeNvIl1C_)HN3+YCwFE`GMWj(8}=0k(vyyUAN=R z&zyl4w}X!y;caLAv2_QO-aT>z#BFI}c=M6V%1ghxzwbdO%};5bRoNKeK0R~q68mGX zk@#2ft!@<`hNP1(IU2ssQ#O}zw$k8bkK$x6HB$P`=S7x~ncAJFkA>8DRE=)8b2N{u ztdvw$!4{U5mU1g#ml=zTyIY&O!7mS*iagh92PbRiUo?StN#U!EeKe5<4qk;28Dk9J z=ARg@7B1z+R};-4WY_?oG@RcEpni{R0V!@$ZeDo3I-8;*S+iIf04005rBCb?t~~;WgKZ^xZ3J$ZS5eq zMUT>_UXUKR#6x3^A`NGA5Is3$@1aoN@ycbW287OteMP048R=1GmcoElXwP%?Xkbr4uMR&(KCQpZkXU1A-C_R zffpR-SZV3B=3R#GVe}@yI>UB;5an`RzCDEJ0E|bL2~EMz8+SHx(fYPJCUDckRK?Pa zl`)c0KR>w?$87JchN1OzrRVCdf=ZKR$91jRCp({a$EZLTuk?=JDn0F@aRMNjq{{w! z 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.0.0,<7.0.0 + ESP Async WebServer@>=1.2.0,<2.0.0 + AsyncMqttClient@>=0.8.2,<1.0.0 + +[env:esp12e] +platform = espressif8266 +board = esp12e +board_build.f_cpu = 160000000L +board_build.filesystem = littlefs + +[env:node32s] +; Comment out min_spiffs.csv setting if disabling PROGMEM_WWW with ESP32 +board_build.partitions = min_spiffs.csv +platform = espressif32 +board = node32s diff --git a/scripts/build_interface.py b/scripts/build_interface.py new file mode 100644 index 0000000..84b7c1d --- /dev/null +++ b/scripts/build_interface.py @@ -0,0 +1,34 @@ +from pathlib import Path +from shutil import copytree +from shutil import rmtree +from subprocess import check_output, Popen, PIPE, STDOUT, CalledProcessError +from os import chdir + +Import("env") + +def flagExists(flag): + buildFlags = env.ParseFlags(env["BUILD_FLAGS"]) + for define in buildFlags.get("CPPDEFINES"): + if (define == flag or (isinstance(define, list) and define[0] == flag)): + return True + +def buildWeb(): + chdir("interface") + print("Building interface with npm") + try: + env.Execute("npm install") + env.Execute("npm run build") + buildPath = Path("build") + wwwPath = Path("../data/www") + if wwwPath.exists() and wwwPath.is_dir(): + rmtree(wwwPath) + if not flagExists("PROGMEM_WWW"): + print("Copying interface to data directory") + copytree(buildPath, wwwPath) + finally: + chdir("..") + +if (len(BUILD_TARGETS) == 0 or "upload" in BUILD_TARGETS): + buildWeb() +else: + print("Skipping build interface step for target(s): " + ", ".join(BUILD_TARGETS)) diff --git a/src/LightMqttSettingsService.cpp b/src/LightMqttSettingsService.cpp new file mode 100644 index 0000000..bddd909 --- /dev/null +++ b/src/LightMqttSettingsService.cpp @@ -0,0 +1,16 @@ +#include + +LightMqttSettingsService::LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : + _httpEndpoint(LightMqttSettings::read, + LightMqttSettings::update, + this, + server, + LIGHT_BROKER_SETTINGS_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _fsPersistence(LightMqttSettings::read, LightMqttSettings::update, this, fs, LIGHT_BROKER_SETTINGS_FILE) { +} + +void LightMqttSettingsService::begin() { + _fsPersistence.readFromFS(); +} diff --git a/src/LightMqttSettingsService.h b/src/LightMqttSettingsService.h new file mode 100644 index 0000000..e7b66b0 --- /dev/null +++ b/src/LightMqttSettingsService.h @@ -0,0 +1,41 @@ +#ifndef LightMqttSettingsService_h +#define LightMqttSettingsService_h + +#include +#include +#include + +#define LIGHT_BROKER_SETTINGS_FILE "/config/brokerSettings.json" +#define LIGHT_BROKER_SETTINGS_PATH "/rest/brokerSettings" + +class LightMqttSettings { + public: + String mqttPath; + String name; + String uniqueId; + + static void read(LightMqttSettings& settings, JsonObject& root) { + root["mqtt_path"] = settings.mqttPath; + root["name"] = settings.name; + root["unique_id"] = settings.uniqueId; + } + + static StateUpdateResult update(JsonObject& root, LightMqttSettings& settings) { + settings.mqttPath = root["mqtt_path"] | SettingValue::format("homeassistant/light/#{unique_id}"); + settings.name = root["name"] | SettingValue::format("light-#{unique_id}"); + settings.uniqueId = root["unique_id"] | SettingValue::format("light-#{unique_id}"); + return StateUpdateResult::CHANGED; + } +}; + +class LightMqttSettingsService : public StatefulService { + public: + LightMqttSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); + void begin(); + + private: + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; +}; + +#endif // end LightMqttSettingsService_h diff --git a/src/LightStateService.cpp b/src/LightStateService.cpp new file mode 100644 index 0000000..8169622 --- /dev/null +++ b/src/LightStateService.cpp @@ -0,0 +1,73 @@ +#include + +LightStateService::LightStateService(AsyncWebServer* server, + SecurityManager* securityManager, + AsyncMqttClient* mqttClient, + LightMqttSettingsService* lightMqttSettingsService) : + _httpEndpoint(LightState::read, + LightState::update, + this, + server, + LIGHT_SETTINGS_ENDPOINT_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _mqttPubSub(LightState::haRead, LightState::haUpdate, this, mqttClient), + _webSocket(LightState::read, + LightState::update, + this, + server, + LIGHT_SETTINGS_SOCKET_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED), + _mqttClient(mqttClient), + _lightMqttSettingsService(lightMqttSettingsService) { + // configure led to be output + pinMode(LED_PIN, OUTPUT); + + // configure MQTT callback + _mqttClient->onConnect(std::bind(&LightStateService::registerConfig, this)); + + // configure update handler for when the light settings change + _lightMqttSettingsService->addUpdateHandler([&](const String& originId) { registerConfig(); }, false); + + // configure settings service update handler to update LED state + addUpdateHandler([&](const String& originId) { onConfigUpdated(); }, false); +} + +void LightStateService::begin() { + _state.ledOn = DEFAULT_LED_STATE; + onConfigUpdated(); +} + +void LightStateService::onConfigUpdated() { + digitalWrite(LED_PIN, _state.ledOn ? LED_ON : LED_OFF); +} + +void LightStateService::registerConfig() { + if (!_mqttClient->connected()) { + return; + } + String configTopic; + String subTopic; + String pubTopic; + + DynamicJsonDocument doc(256); + _lightMqttSettingsService->read([&](LightMqttSettings& settings) { + configTopic = settings.mqttPath + "/config"; + subTopic = settings.mqttPath + "/set"; + pubTopic = settings.mqttPath + "/state"; + doc["~"] = settings.mqttPath; + doc["name"] = settings.name; + doc["unique_id"] = settings.uniqueId; + }); + doc["cmd_t"] = "~/set"; + doc["stat_t"] = "~/state"; + doc["schema"] = "json"; + doc["brightness"] = false; + + String payload; + serializeJson(doc, payload); + _mqttClient->publish(configTopic.c_str(), 0, false, payload.c_str()); + + _mqttPubSub.configureTopics(pubTopic, subTopic); +} diff --git a/src/LightStateService.h b/src/LightStateService.h new file mode 100644 index 0000000..e9c9a9c --- /dev/null +++ b/src/LightStateService.h @@ -0,0 +1,88 @@ +#ifndef LightStateService_h +#define LightStateService_h + +#include + +#include +#include +#include + +#define LED_PIN 2 +#define PRINT_DELAY 5000 + +#define DEFAULT_LED_STATE false +#define OFF_STATE "OFF" +#define ON_STATE "ON" + +// Note that the built-in LED is on when the pin is low on most NodeMCU boards. +// This is because the anode is tied to VCC and the cathode to the GPIO 4 (Arduino pin 2). +#ifdef ESP32 +#define LED_ON 0x1 +#define LED_OFF 0x0 +#elif defined(ESP8266) +#define LED_ON 0x0 +#define LED_OFF 0x1 +#endif + +#define LIGHT_SETTINGS_ENDPOINT_PATH "/rest/lightState" +#define LIGHT_SETTINGS_SOCKET_PATH "/ws/lightState" + +class LightState { + public: + bool ledOn; + + static void read(LightState& settings, JsonObject& root) { + root["led_on"] = settings.ledOn; + } + + static StateUpdateResult update(JsonObject& root, LightState& lightState) { + boolean newState = root["led_on"] | DEFAULT_LED_STATE; + if (lightState.ledOn != newState) { + lightState.ledOn = newState; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; + } + + static void haRead(LightState& settings, JsonObject& root) { + root["state"] = settings.ledOn ? ON_STATE : OFF_STATE; + } + + static StateUpdateResult haUpdate(JsonObject& root, LightState& lightState) { + String state = root["state"]; + // parse new led state + boolean newState = false; + if (state.equals(ON_STATE)) { + newState = true; + } else if (!state.equals(OFF_STATE)) { + return StateUpdateResult::ERROR; + } + // change the new state, if required + if (lightState.ledOn != newState) { + lightState.ledOn = newState; + return StateUpdateResult::CHANGED; + } + return StateUpdateResult::UNCHANGED; + } +}; + +class LightStateService : public StatefulService { + public: + LightStateService(AsyncWebServer* server, + SecurityManager* securityManager, + AsyncMqttClient* mqttClient, + LightMqttSettingsService* lightMqttSettingsService); + void begin(); + + private: + HttpEndpoint _httpEndpoint; + MqttPubSub _mqttPubSub; + WebSocketTxRx _webSocket; + AsyncMqttClient* _mqttClient; + LightMqttSettingsService* _lightMqttSettingsService; + + void registerConfig(); + void onConfigUpdated(); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0b1081a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#define SERIAL_BAUD_RATE 115200 + +AsyncWebServer server(80); +ESP8266React esp8266React(&server); +LightMqttSettingsService lightMqttSettingsService = + LightMqttSettingsService(&server, esp8266React.getFS(), esp8266React.getSecurityManager()); +LightStateService lightStateService = LightStateService(&server, + esp8266React.getSecurityManager(), + esp8266React.getMqttClient(), + &lightMqttSettingsService); + +void setup() { + // start serial and filesystem + Serial.begin(SERIAL_BAUD_RATE); + + // start the framework and demo project + esp8266React.begin(); + + // load the initial light settings + lightStateService.begin(); + + // start the light service + lightMqttSettingsService.begin(); + + // start the server + server.begin(); +} + +void loop() { + // run the framework's loop function + esp8266React.loop(); +}