Merge "Add product_available to VNDK snapshots" am: 7e8fcca44e am: f5972653c8 am: a3057e5d7d

Original change: https://android-review.googlesource.com/c/platform/development/+/1512737

Change-Id: I337f5da9e2883fa5ec19f97b8b386a03d09cdde3
diff --git a/apps/Development/AndroidManifest.xml b/apps/Development/AndroidManifest.xml
index 2d5e4b3..c5133a5 100644
--- a/apps/Development/AndroidManifest.xml
+++ b/apps/Development/AndroidManifest.xml
@@ -54,28 +54,32 @@
         <uses-library android:name="android.test.runner" />
 
         <activity android:name="Development" android:label="Dev Tools"
-            android:icon="@mipmap/ic_launcher_devtools">
+            android:icon="@mipmap/ic_launcher_devtools" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name="PackageBrowser" android:label="Package Browser">
+        <activity android:name="PackageBrowser" android:label="Package Browser"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
-        <activity android:name="PackageSummary" android:label="Package Summary">
+        <activity android:name="PackageSummary" android:label="Package Summary"
+            android:exported="true">
         </activity>
-        <activity android:name="ShowActivity" android:label="Activity Detail">
+        <activity android:name="ShowActivity" android:label="Activity Detail"
+            android:exported="true">
         </activity>
         <activity android:name="AppPicker"
-                android:theme="@android:style/Theme.Dialog">
+                android:theme="@android:style/Theme.Dialog" android:exported="true">
         </activity>
         <activity android:name="PointerLocation" android:label="Pointer Location"
                 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
-                android:configChanges="keyboard|keyboardHidden|navigation|orientation">
+                android:configChanges="keyboard|keyboardHidden|navigation|orientation"
+                android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
@@ -83,39 +87,42 @@
         </activity>
 
         <activity android:name="AccountsTester" android:label="AccountsTester"
-                  android:windowSoftInputMode="stateHidden">
+                  android:windowSoftInputMode="stateHidden" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
 
-        <activity android:name="SyncAdapterDriver" android:label="Sync Tester">
+        <activity android:name="SyncAdapterDriver" android:label="Sync Tester"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
 
-        <activity android:name="DataList">
+        <activity android:name="DataList" android:exported="true">
         </activity>
-        <activity android:name="Details">
+        <activity android:name="Details" android:exported="true">
         </activity>
-        <activity android:name="Connectivity" android:label="Connectivity" >
+        <activity android:name="Connectivity" android:label="Connectivity" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
 
-        <activity android:name="InstrumentationList" android:label="Instrumentation">
+        <activity android:name="InstrumentationList" android:label="Instrumentation"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
 
-        <activity android:name="MediaScannerActivity" android:label="Media Provider">
+        <activity android:name="MediaScannerActivity" android:label="Media Provider"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
@@ -131,13 +138,15 @@
         </activity>
     -->
 
-        <activity android:name="RunningProcesses" android:label="Running processes">
+        <activity android:name="RunningProcesses" android:label="Running processes"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
-        <activity android:name="ProcessInfo" android:label="Process Information">
+        <activity android:name="ProcessInfo" android:label="Process Information"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -151,40 +160,44 @@
             </intent-filter>
         </activity>
     -->
-        <activity android:name="AppHwPref" android:label="Applications Hardware Preferences">
+        <activity android:name="AppHwPref" android:label="Applications Hardware Preferences"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name="PermissionDetails" android:label="Permission Info">
+        <activity android:name="PermissionDetails" android:label="Permission Info"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.development.VIEW_PERMISSION" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
 
-        <activity android:name="BadBehaviorActivity" android:label="Bad Behavior">
+        <activity android:name="BadBehaviorActivity" android:label="Bad Behavior"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
-        <receiver android:name="BadBehaviorActivity$BadReceiver">
+        <receiver android:name="BadBehaviorActivity$BadReceiver" android:exported="true">
             <intent-filter>
                 <action android:name="com.android.development.BAD_BEHAVIOR" />
             </intent-filter>
         </receiver>
         <service android:name="BadBehaviorActivity$BadService" />
 
-        <activity android:name="CacheAbuser" android:label="Cache Abuser">
+        <activity android:name="CacheAbuser" android:label="Cache Abuser" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
 
-        <activity android:name="ConfigurationViewer" android:label="Configuration">
+        <activity android:name="ConfigurationViewer" android:label="Configuration"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
index d612c34..2f22779 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyNetworkMonitor.java
@@ -85,8 +85,8 @@
 
     public void register(IActivityManager am) throws RemoteException {
         if (LDEBUG) Logger.out.println("registering Receiver");
-        am.registerReceiverWithFeature(null, null, null, this, filter, null, UserHandle.USER_ALL,
-                0);
+        am.registerReceiverWithFeature(null, null, null, null, this, filter, null,
+                UserHandle.USER_ALL, 0);
     }
 
     public void unregister(IActivityManager am) throws RemoteException {
diff --git a/ide/clion/frameworks/base/CMakeLists.txt b/ide/clion/frameworks/base/CMakeLists.txt
index 70566c4..14b8fb3 100644
--- a/ide/clion/frameworks/base/CMakeLists.txt
+++ b/ide/clion/frameworks/base/CMakeLists.txt
@@ -9,4 +9,3 @@
 
 # JNI
 add_subdirectory(core/jni/libandroid_runtime-arm64-android)
-add_subdirectory(core/jni/libandroid_graphics-arm64-android)
diff --git a/ide/clion/frameworks/native/CMakeLists.txt b/ide/clion/frameworks/native/CMakeLists.txt
index 869910f..2b65c15 100644
--- a/ide/clion/frameworks/native/CMakeLists.txt
+++ b/ide/clion/frameworks/native/CMakeLists.txt
@@ -2,13 +2,14 @@
 project(native)
 add_subdirectory(libs/gui/libgui-arm64-android)
 add_subdirectory(libs/ui/libui-arm64-android)
+add_subdirectory(libs/renderengine/librenderengine-arm64-android)
 add_subdirectory(services/surfaceflinger/surfaceflinger-arm64-android)
 add_subdirectory(services/surfaceflinger/CompositionEngine/libcompositionengine_mocks-arm64-android)
 add_subdirectory(services/surfaceflinger/CompositionEngine/libcompositionengine_test-arm64-android)
 add_subdirectory(services/surfaceflinger/CompositionEngine/libcompositionengine-arm64-android)
+add_subdirectory(services/surfaceflinger/FrameTimeline/libframetimeline-arm64-android)
 add_subdirectory(services/surfaceflinger/TimeStats/timestatsproto/libtimestats_proto-arm64-android)
 add_subdirectory(services/surfaceflinger/TimeStats/libtimestats-arm64-android)
-add_subdirectory(services/surfaceflinger/libsurfaceflinger-arm64-android)
 add_subdirectory(services/surfaceflinger/layerproto/liblayers_proto-arm64-android)
 add_subdirectory(services/surfaceflinger/tests/IPC_test-arm64-android)
 add_subdirectory(services/surfaceflinger/tests/fakehwc/sffakehwc_test-arm64-android)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java b/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
index bc1b46f..5f2dbc3 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
@@ -83,15 +83,16 @@
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        if (ACTION_FOREGROUND.equals(intent.getAction())
-                || ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction())) {
+        final boolean usingWakelock = ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction());
+        if (ACTION_FOREGROUND.equals(intent.getAction()) || usingWakelock) {
             // In this sample, we'll use the same text for the ticker and the expanded notification
             CharSequence text = getText(R.string.foreground_service_started);
 
             PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                     new Intent(this, Controller.class), 0);
 
-            // Set the info for the views that show in the notification panel.
+            // Set the info for the views that show in the notification panel.  In the
+            // wakelock flow, also force the notification to display immediately.
             Notification notification = new Notification.Builder(this)
                     .setSmallIcon(R.drawable.stat_sample)  // the status icon
                     .setTicker(text)  // the status text
@@ -99,6 +100,7 @@
                     .setContentTitle(getText(R.string.alarm_service_label))  // the label
                     .setContentText(text)  // the contents of the entry
                     .setContentIntent(contentIntent)  // The intent to send when clicked
+                    .setShowForegroundImmediately(usingWakelock)
                     .build();
 
             startForeground(R.string.foreground_service_started, notification);
diff --git a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java
index 242e7ee..f920a2d 100644
--- a/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java
+++ b/samples/ApiDemos/src/com/example/android/mmslib/pdu/PduComposer.java
@@ -1012,7 +1012,7 @@
             }
 
             if (dataLength != (attachment.getLength() - headerLength)) {
-                throw new RuntimeException("BUG: Length sanity check failed");
+                throw new RuntimeException("BUG: Length check failed");
             }
 
             mStack.pop();
diff --git a/samples/BasicGLSurfaceView/AndroidManifest.xml b/samples/BasicGLSurfaceView/AndroidManifest.xml
index b0375aa..14c84b2 100644
--- a/samples/BasicGLSurfaceView/AndroidManifest.xml
+++ b/samples/BasicGLSurfaceView/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <application
             android:label="@string/app_name">
         <activity android:name="BasicGLSurfaceViewActivity"
+                android:exported="true"
                 android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                 android:launchMode="singleTask"
                 android:configChanges="orientation|keyboardHidden">
diff --git a/samples/BusinessCard/AndroidManifest.xml b/samples/BusinessCard/AndroidManifest.xml
index 186e249..683ab3f 100644
--- a/samples/BusinessCard/AndroidManifest.xml
+++ b/samples/BusinessCard/AndroidManifest.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-  
+
           http://www.apache.org/licenses/LICENSE-2.0
-  
+
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,14 +16,15 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.businesscard">
-    
+
     <!-- IMPORTANT!  We want this app to run on Cupcake, Donut, Eclair and beyond -->
     <uses-sdk android:minSdkVersion="3"/>
-    
+
     <uses-permission android:name="android.permission.READ_CONTACTS" />
-    
+
     <application android:label="@string/businesscard_app">
-        <activity android:name="BusinessCardActivity">
+        <activity android:name="BusinessCardActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/samples/HelloActivity/AndroidManifest.xml b/samples/HelloActivity/AndroidManifest.xml
index 9551e54..2f01c10 100644
--- a/samples/HelloActivity/AndroidManifest.xml
+++ b/samples/HelloActivity/AndroidManifest.xml
@@ -22,7 +22,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.helloactivity">
     <application android:label="Hello, Activity!">
-        <activity android:name="HelloActivity">
+        <activity android:name="HelloActivity"
+                android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
diff --git a/samples/InlineFillService/AndroidManifest.xml b/samples/InlineFillService/AndroidManifest.xml
index fa002f1..6b40863 100644
--- a/samples/InlineFillService/AndroidManifest.xml
+++ b/samples/InlineFillService/AndroidManifest.xml
@@ -5,6 +5,7 @@
         <service
             android:name=".InlineFillService"
             android:label="Inline Fill Service"
+            android:exported="true"
             android:permission="android.permission.BIND_AUTOFILL_SERVICE">
             <intent-filter>
                 <action android:name="android.service.autofill.AutofillService" />
diff --git a/samples/InlineFillService/src/com/example/android/inlinefillservice/AuthActivity.java b/samples/InlineFillService/src/com/example/android/inlinefillservice/AuthActivity.java
index cb9cf88..9d9ed7a 100644
--- a/samples/InlineFillService/src/com/example/android/inlinefillservice/AuthActivity.java
+++ b/samples/InlineFillService/src/com/example/android/inlinefillservice/AuthActivity.java
@@ -114,6 +114,6 @@
         }
 
         return PendingIntent.getActivity(context, ++sPendingIntentId, intent,
-                PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE).getIntentSender();
     }
 }
diff --git a/samples/InlineFillService/src/com/example/android/inlinefillservice/InlineRequestHelper.java b/samples/InlineFillService/src/com/example/android/inlinefillservice/InlineRequestHelper.java
index c93771a..0aff9d2 100644
--- a/samples/InlineFillService/src/com/example/android/inlinefillservice/InlineRequestHelper.java
+++ b/samples/InlineFillService/src/com/example/android/inlinefillservice/InlineRequestHelper.java
@@ -79,7 +79,7 @@
             InlineSuggestionsRequest inlineRequest, int drawable) {
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(context, 0, new Intent(context, SettingsActivity.class),
-                        PendingIntent.FLAG_UPDATE_CURRENT);
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
         Dataset.Builder builder =
                 new Dataset.Builder()
@@ -126,9 +126,8 @@
         Intent intent = new Intent(context, AttributionDialogActivity.class);
         intent.putExtra(AttributionDialogActivity.KEY_MSG, msg);
         // Should use different request code to avoid the new intent overriding the old one.
-        PendingIntent pendingIntent =
-                PendingIntent.getActivity(
-                        context, msg.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, msg.hashCode(), intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
         return pendingIntent;
     }
 
diff --git a/samples/IntentPlayground/AndroidManifest.xml b/samples/IntentPlayground/AndroidManifest.xml
index d810d8b..aa934fa 100644
--- a/samples/IntentPlayground/AndroidManifest.xml
+++ b/samples/IntentPlayground/AndroidManifest.xml
@@ -24,7 +24,7 @@
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
-        <activity android:name=".RegularActivity">
+        <activity android:name=".RegularActivity" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/samples/ReceiveContentDemo/Android.bp b/samples/ReceiveContentDemo/Android.bp
new file mode 100644
index 0000000..a959bd8
--- /dev/null
+++ b/samples/ReceiveContentDemo/Android.bp
@@ -0,0 +1,12 @@
+android_app {
+    name: "ReceiveContentDemo",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "guava",
+        "jsr305",
+    ],
+    sdk_version: "current",
+    dex_preopt: {
+        enabled: false,
+    },
+}
diff --git a/samples/ReceiveContentDemo/AndroidManifest.xml b/samples/ReceiveContentDemo/AndroidManifest.xml
new file mode 100644
index 0000000..e765a0f
--- /dev/null
+++ b/samples/ReceiveContentDemo/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Declare the contents of this Android application.  The namespace
+     attribute brings in the Android platform namespace, and the package
+     supplies a unique name for the application.  When writing your
+     own application, the package name must be changed from "com.example.*"
+     to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.example.android.receivecontent">
+
+    <application android:label="@string/app_name">
+        <activity android:name=".ReceiveContentDemoActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/samples/ReceiveContentDemo/README.md b/samples/ReceiveContentDemo/README.md
new file mode 100644
index 0000000..936cb4a
--- /dev/null
+++ b/samples/ReceiveContentDemo/README.md
@@ -0,0 +1,7 @@
+Sample app that demonstrates how to implement support for receiving content (e.g. images) via paste
+from the long-press menu, drag-and-drop, etc.
+
+To build and run:
+```$bash
+m ReceiveContentDemo && adb install out/target/product/blueline/system/app/ReceiveContentDemo/ReceiveContentDemo.apk
+```
diff --git a/samples/ReceiveContentDemo/res/layout/demo.xml b/samples/ReceiveContentDemo/res/layout/demo.xml
new file mode 100644
index 0000000..6dbb162
--- /dev/null
+++ b/samples/ReceiveContentDemo/res/layout/demo.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <EditText
+        android:id="@+id/edittext_no_callback"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:hint="@string/hint_edittext_no_callback"/>
+
+    <EditText
+        android:id="@+id/edittext_images"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="top"
+        android:hint="@string/hint_edittext_images"/>
+
+    <EditText
+        android:id="@+id/edittext_all_types"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="top"
+        android:hint="@string/hint_edittext_all_types"/>
+</LinearLayout>
diff --git a/samples/ReceiveContentDemo/res/values/strings.xml b/samples/ReceiveContentDemo/res/values/strings.xml
new file mode 100644
index 0000000..57c7a60
--- /dev/null
+++ b/samples/ReceiveContentDemo/res/values/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <string name="app_name">Receive Content Demo</string>
+    <string name="hint_edittext_no_callback">Default EditText</string>
+    <string name="hint_edittext_images">
+        EditText with a listener that handles images
+    </string>
+    <string name="hint_edittext_all_types">
+        EditText with a listener that handles all content
+    </string>
+</resources>
diff --git a/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyExecutors.java b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyExecutors.java
new file mode 100644
index 0000000..e2c73b2
--- /dev/null
+++ b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyExecutors.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.receivecontent;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class MyExecutors {
+    private static final ExecutorService mBg = Executors.newSingleThreadExecutor();
+
+    public static ExecutorService getBg() {
+        return mBg;
+    }
+}
diff --git a/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerAllContent.java b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerAllContent.java
new file mode 100644
index 0000000..d337e26
--- /dev/null
+++ b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerAllContent.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.receivecontent;
+
+import static com.example.android.receivecontent.ReceiveContentDemoActivity.LOG_TAG;
+import static com.example.android.receivecontent.Utils.matchesAny;
+import static com.example.android.receivecontent.Utils.showMessage;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.Log;
+import android.view.OnReceiveContentListener;
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * Sample implementation that:
+ * <ul>
+ *     <li>Accepts images and mp4 videos.
+ *     <li>Rejects other content URIs.
+ *     <li>Coerces all other content to lower-case, plain text and delegates its insertion to the
+ *     platform.
+ * </ul>
+ */
+public class MyListenerAllContent implements OnReceiveContentListener {
+    static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*", "video/mp4"};
+
+    @Override
+    public Payload onReceiveContent(View view, Payload payload) {
+        ClipData clip = payload.getClip();
+        ClipDescription description = clip.getDescription();
+        ArrayList<ClipData.Item> remainingItems = new ArrayList<>();
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                receive(view, uri);
+                continue;
+            }
+            CharSequence text = item.coerceToText(view.getContext());
+            text = text.toString().toLowerCase();
+            remainingItems.add(new ClipData.Item(
+                    text, item.getHtmlText(), item.getIntent(), item.getUri()));
+        }
+
+        if (!remainingItems.isEmpty()) {
+            Log.i(LOG_TAG, "Delegating " + remainingItems.size() + " item(s) to platform");
+            ClipData newClip = new ClipData(description, remainingItems.get(0));
+            for (int i = 1; i < remainingItems.size(); i++) {
+                newClip.addItem(remainingItems.get(i));
+            }
+            return new Payload.Builder(payload).setClip(newClip).build();
+        }
+
+        return null;
+    }
+
+    private static void receive(View view, Uri contentUri) {
+        MyExecutors.getBg().submit(() -> {
+            ContentResolver contentResolver = view.getContext().getContentResolver();
+            String mimeType = contentResolver.getType(contentUri);
+            if (!matchesAny(mimeType, SUPPORTED_MIME_TYPES)) {
+                showMessage(view, "Content of type " + mimeType + "  is not supported");
+                return;
+            }
+            showMessage(view, "Received " + mimeType + ": " + contentUri);
+        });
+    }
+}
diff --git a/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerImages.java b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerImages.java
new file mode 100644
index 0000000..3510a04
--- /dev/null
+++ b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/MyListenerImages.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.receivecontent;
+
+import static com.example.android.receivecontent.Utils.matchesAny;
+import static com.example.android.receivecontent.Utils.showMessage;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.view.OnReceiveContentListener;
+import android.view.View;
+
+import java.util.Map;
+
+/**
+ * Sample implementation that accepts images, rejects other URIs, and delegates handling for all
+ * non-URI content to the platform.
+ */
+public class MyListenerImages implements OnReceiveContentListener {
+    static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
+
+    @Override
+    public Payload onReceiveContent(View view, Payload payload) {
+        Map<Boolean, Payload> split = payload.partition(item -> item.getUri() != null);
+        if (split.get(true) != null) {
+            ClipData clip = payload.getClip();
+            for (int i = 0; i < clip.getItemCount(); i++) {
+                receive(view, clip.getItemAt(i).getUri());
+            }
+        }
+        return split.get(false);
+    }
+
+    private static void receive(View view, Uri contentUri) {
+        MyExecutors.getBg().submit(() -> {
+            ContentResolver contentResolver = view.getContext().getContentResolver();
+            String mimeType = contentResolver.getType(contentUri);
+            if (!matchesAny(mimeType, SUPPORTED_MIME_TYPES)) {
+                showMessage(view, "Content of type " + mimeType + "  is not supported");
+                return;
+            }
+            showMessage(view, "Received " + mimeType + ": " + contentUri);
+        });
+    }
+}
diff --git a/samples/ReceiveContentDemo/src/com/example/android/receivecontent/ReceiveContentDemoActivity.java b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/ReceiveContentDemoActivity.java
new file mode 100644
index 0000000..76eaf19
--- /dev/null
+++ b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/ReceiveContentDemoActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.receivecontent;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.EditText;
+
+public class ReceiveContentDemoActivity extends Activity {
+    public static final String LOG_TAG = "ReceiveContentDemo";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.demo);
+
+        EditText editTextImagesOnly = findViewById(R.id.edittext_images);
+        editTextImagesOnly.setOnReceiveContentListener(
+                MyListenerImages.SUPPORTED_MIME_TYPES,
+                new MyListenerImages());
+
+        EditText editTextAllTypes = findViewById(R.id.edittext_all_types);
+        editTextAllTypes.setOnReceiveContentListener(
+                MyListenerAllContent.SUPPORTED_MIME_TYPES,
+                new MyListenerAllContent());
+    }
+}
diff --git a/samples/ReceiveContentDemo/src/com/example/android/receivecontent/Utils.java b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/Utils.java
new file mode 100644
index 0000000..10860b2
--- /dev/null
+++ b/samples/ReceiveContentDemo/src/com/example/android/receivecontent/Utils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.receivecontent;
+
+import static com.example.android.receivecontent.ReceiveContentDemoActivity.LOG_TAG;
+
+import android.content.ClipDescription;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+final class Utils {
+    private Utils() {}
+
+    public static boolean matchesAny(String mimeType, String[] targetMimeTypes) {
+        for (String targetMimeType : targetMimeTypes) {
+            if (ClipDescription.compareMimeTypes(mimeType, targetMimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static void showMessage(View view, String msg) {
+        Log.i(LOG_TAG, msg);
+        view.getHandler().post(() ->
+                Toast.makeText(view.getContext(), msg, Toast.LENGTH_LONG).show()
+        );
+    }
+}
diff --git a/samples/ReceiveShareDemo/AndroidManifest.xml b/samples/ReceiveShareDemo/AndroidManifest.xml
index 1e07091..97ce54f 100644
--- a/samples/ReceiveShareDemo/AndroidManifest.xml
+++ b/samples/ReceiveShareDemo/AndroidManifest.xml
@@ -17,7 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.receiveshare">
     <application android:label="@string/app_name">
-        <activity android:name=".ReceiveShare">
+        <activity android:name=".ReceiveShare" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/samples/SpellChecker/HelloSpellChecker/AndroidManifest.xml b/samples/SpellChecker/HelloSpellChecker/AndroidManifest.xml
index 0a6b906..e6cd564 100644
--- a/samples/SpellChecker/HelloSpellChecker/AndroidManifest.xml
+++ b/samples/SpellChecker/HelloSpellChecker/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <application
         android:label="@string/app_name" >
         <activity
+            android:exported="true"
             android:label="@string/app_name"
             android:name=".HelloSpellCheckerActivity" >
             <intent-filter >
diff --git a/samples/SpellChecker/SampleSpellCheckerService/AndroidManifest.xml b/samples/SpellChecker/SampleSpellCheckerService/AndroidManifest.xml
index fcb1671..1042e3f 100644
--- a/samples/SpellChecker/SampleSpellCheckerService/AndroidManifest.xml
+++ b/samples/SpellChecker/SampleSpellCheckerService/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <application
         android:label="@string/app_name" >
         <service
+            android:exported="false"
             android:label="@string/app_name"
             android:name=".SampleSpellCheckerService"
             android:permission="android.permission.BIND_TEXT_SERVICE" >
@@ -36,11 +37,9 @@
         </service>
 
         <activity
+            android:exported="false"
             android:label="@string/sample_settings"
             android:name="SpellCheckerSettingsActivity" >
-            <intent-filter >
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
         </activity>
     </application>
 
diff --git a/tools/privapp_permissions/privapp_permissions.py b/tools/privapp_permissions/privapp_permissions.py
index d61319e..6e8451c 100755
--- a/tools/privapp_permissions/privapp_permissions.py
+++ b/tools/privapp_permissions/privapp_permissions.py
@@ -37,12 +37,19 @@
 
 Usage:
     Specify which apk to generate priv-app permissions for. If no apk is \
-specified, this will default to all APKs under "<ANDROID_PRODUCT_OUT>/ \
-system/priv-app and (system/)product/priv-app".
+specified, this will default to all APKs under "<ANDROID_PRODUCT_OUT>/\
+<all the partitions>/priv-app/".
+
+    To specify a target partition(s), use "-p <PARTITION>," where <PARTITION> \
+can be "system", "product", "system/product", "system_ext", \
+"system/system_ext", "system,system/product,vendor,system_ext", etc.
+
+    When using adb, adb pull can take a long time. To see the adb pull \
+progress, use "-v"
 
 Examples:
 
-    For all APKs under $ANDROID_PRODUCT_OUT:
+    For all APKs under $ANDROID_PRODUCT_OUT/<all partitions>/priv-app/:
         # If the build environment has not been set up, do so:
         . build/envsetup.sh
         lunch product_name
@@ -50,17 +57,27 @@
         # then use:
         cd development/tools/privapp_permissions/
         ./privapp_permissions.py
+        # or to search for apks in "product" partition
+        ./privapp_permissions.py -p product
+        # or to search for apks in system, product, and vendor partitions
+        ./privapp_permissions.py -p system,product,vendor
 
-    For a given apk:
+    For an APK against $ANDROID_PRODUCT_OUT/<all partitions>/etc/permissions/:
         ./privapp_permissions.py path/to/the.apk
+        # or against /product/etc/permissions/
+        ./privapp_permissions.py path/to/the.apk -p product
 
-    For an APK already on the device:
+    For an APK already on the device against /<all partitions>/etc/permissions/:
         ./privapp_permissions.py device:/device/path/to/the.apk
+        # or against /product/etc/permissions/
+        ./privapp_permissions.py path/to/the.apk -p product
 
-    For all APKs on a device:
+    For all APKs on a device under /<all partitions>/priv-app/:
         ./privapp_permissions.py -d
         # or if more than one device is attached
-        ./privapp_permissions.py -s <ANDROID_SERIAL>\
+        ./privapp_permissions.py -s <ANDROID_SERIAL>
+        # or for all APKs on the "system" partitions
+        ./privapp_permissions.py -d -p system
 """
 
 # An array of all generated temp directories.
@@ -68,6 +85,10 @@
 # An array of all generated temp files.
 temp_files = []
 
+def vprint(enable, message, *args):
+    if enable:
+        # Use stderr to avoid poluting print_xml result
+        sys.stderr.write(message % args + '\n')
 
 class MissingResourceError(Exception):
     """Raised when a dependency cannot be located."""
@@ -76,9 +97,10 @@
 class Adb(object):
     """A small wrapper around ADB calls."""
 
-    def __init__(self, path, serial=None):
+    def __init__(self, path, serial=None, verbose=False):
         self.path = path
         self.serial = serial
+        self.verbose = verbose
 
     def pull(self, src, dst=None):
         """A wrapper for `adb -s <SERIAL> pull <src> <dst>`.
@@ -96,18 +118,26 @@
             else:
                 _, dst = tempfile.mkstemp()
                 temp_files.append(dst)
-        self.call('pull %s %s' % (src, dst))
+        self.call('pull %s %s' % (src, dst), False, self.verbose)
         return dst
 
-    def call(self, cmdline):
+    def call(self, cmdline, getoutput=True, verbose=False):
         """Calls an adb command.
 
         Throws:
             subprocess.CalledProcessError upon command failure.
         """
         command = '%s -s %s %s' % (self.path, self.serial, cmdline)
-        return get_output(command)
-
+        if getoutput:
+            return get_output(command)
+        else:
+            # Handle verbose mode only when the output is not needed
+            # This is mainly for adb pull, which can take a long time
+            extracmd = ' > /dev/null 2>&1'
+            if verbose:
+                # Use stderr to avoid poluting print_xml result
+                extracmd = ' 1>&2'
+            os.system(command + extracmd)
 
 class Aapt(object):
     def __init__(self, path):
@@ -136,10 +166,17 @@
     """
 
     def __init__(self, adb_path=None, aapt_path=None, use_device=None,
-                 serial=None, apks=None):
+                 serial=None, partitions=None, verbose=False,
+                 writetodisk=None, systemfile=None, productfile=None,
+                 apks=None):
         self.adb = Resources._resolve_adb(adb_path)
         self.aapt = Resources._resolve_aapt(aapt_path)
 
+        self.verbose = self.adb.verbose = verbose
+        self.writetodisk = writetodisk
+        self.systemfile = systemfile;
+        self.productfile = productfile;
+
         self._is_android_env = 'ANDROID_PRODUCT_OUT' in os.environ and \
                                'ANDROID_HOST_OUT' in os.environ
         use_device = use_device or serial or \
@@ -156,21 +193,42 @@
                 'You must either set up your build environment, or specify a '
                 'device to run against. See --help for more info.')
 
-        self.system_privapp_apks, self.product_privapp_apks =(
-                self._resolve_apks(apks))
-        self.system_permissions_dir = (
-                self._resolve_sys_path('system/etc/permissions'))
-        self.system_sysconfig_dir = (
-                self._resolve_sys_path('system/etc/sysconfig'))
-        self.product_permissions_dir = (
-                self._resolve_sys_path('product/etc/permissions',
-                                       'system/product/etc/permissions'))
-        self.product_sysconfig_dir = (
-                self._resolve_sys_path('product/etc/sysconfig',
-                                       'system/product/etc/sysconfig'))
+        if apks and (partitions == "all" or partitions.find(',') != -1):
+            # override the partition to "system
+            print('\n# Defaulting the target partition to "system". '
+                  'Use -p option to specify the target partition '
+                  '(must provide one target instead of a list).\n',
+                  file=sys.stderr)
+            partitions = "system"
+
+        if partitions == "all":
+            # This is the default scenario
+            # Find all the partitions where priv-app exists
+            self.partitions = self._get_partitions()
+        else:
+            # Initialize self.partitions with the specified partitions
+            self.partitions = []
+            for p in partitions.split(','):
+                if p.endswith('/'):
+                    p = p[:-1]
+                self.partitions.append(p)
+                # Check if the directory exists
+                self._check_dir(p + '/priv-app')
+
+        vprint(self.verbose,
+                '# Examining the partitions: ' + str(self.partitions))
+
+        # Create dictionary of array (partition as the key)
+        self.privapp_apks = self._resolve_apks(apks, self.partitions)
+        self.permissions_dirs = self._resolve_sys_paths('etc/permissions',
+                                                       self.partitions)
+        self.sysconfig_dirs = self._resolve_sys_paths('etc/sysconfig',
+                                                     self.partitions)
+
+        # Always use the one in /system partition,
+        # as that is the only place we will find framework-res.apk
         self.framework_res_apk = self._resolve_sys_path('system/framework/'
                                                         'framework-res.apk')
-
     @staticmethod
     def _resolve_adb(adb_path):
         """Resolves ADB from either the cmdline argument or the os environment.
@@ -287,21 +345,75 @@
                       'devices.', file=sys.stderr)
                 raise
 
-    def _resolve_apks(self, apks):
+    def _get_partitions(self):
+        """Find all the partitions to examine
+
+        Returns:
+            The array of partitions where priv-app exists
+        Raises:
+            MissingResourceError find command over adb shell fails.
+        """
+        if not self.adb.serial:
+            privapp_dirs = get_output('cd  %s; find * -name "priv-app"'
+                                      % os.environ['ANDROID_PRODUCT_OUT']
+                                      + ' -type d | grep -v obj').split()
+        else:
+            try:
+                privapp_dirs = self.adb.call('shell find \'/!(proc)\' \
+                                           -name "priv-app" -type d').split()
+            except subprocess.CalledProcessError:
+                raise MissingResourceError(
+                    '"adb shell find / -name priv-app -type d" did not succeed'
+                    ' on device "%s".' % self.adb.serial)
+
+        # Remove 'priv-app' from the privapp_dirs
+        partitions = []
+        for i in range(len(privapp_dirs)):
+            partitions.append('/'.join(privapp_dirs[i].split('/')[:-1]))
+
+        return partitions
+
+    def _check_dir(self, directory):
+        """Check if a given directory is valid
+
+        Raises:
+            MissingResourceError if a given directory does not exist.
+        """
+        if not self.adb.serial:
+            if not os.path.isdir(os.environ['ANDROID_PRODUCT_OUT']
+                                 + '/' + directory):
+                raise MissingResourceError(
+                    '%s does not exist' % directory)
+        else:
+            try:
+                self.adb.call('shell ls %s' % directory)
+            except subprocess.CalledProcessError:
+                raise MissingResourceError(
+                    '"adb shell ls %s" did not succeed on '
+                    'device "%s".' % (directory, self.adb.serial))
+
+
+    def _resolve_apks(self, apks, partitions):
         """Resolves all APKs to run against.
 
         Returns:
             If no apk is specified in the arguments, return all apks in
-            system/priv-app. Otherwise, returns a list with the specified apk.
+            priv-app in all the partitions.
+            Otherwise, returns a list with the specified apk.
         Throws:
-            MissingResourceError if the specified apk or system/priv-app cannot
-            be found.
+            MissingResourceError if the specified apk or
+            <partition>/priv-app cannot be found.
         """
+        results = {}
         if not apks:
-            return (self._resolve_all_system_privapps(),
-                   self._resolve_all_product_privapps())
+            for p in partitions:
+                results[p] = self._resolve_all_privapps(p)
+            return results
 
-        ret_apks = []
+        # The first element is what is passed via '-p' option
+        # (default is overwritten to 'system' when apk is specified)
+        p = partitions[0]
+        results[p] = []
         for apk in apks:
             if apk.startswith(DEVICE_PREFIX):
                 device_apk = apk[len(DEVICE_PREFIX):]
@@ -311,82 +423,47 @@
                     raise MissingResourceError(
                         'File "%s" could not be located on device "%s".' %
                         (device_apk, self.adb.serial))
-                ret_apks.append(apk)
+                results[p].append(apk)
             elif not os.path.isfile(apk):
                 raise MissingResourceError('File "%s" does not exist.' % apk)
             else:
-                ret_apks.append(apk)
-        return ret_apks, None
+               results[p].append(apk)
+        return results
 
-    def _resolve_all_system_privapps(self):
-        """Extract package name and requested permissions."""
-        if self._is_android_env:
-            system_priv_app_dir = (
-                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
-                                            'system/priv-app'))
+    def _resolve_all_privapps(self, partition):
+        """Resolves all APKs in <partition>/priv-app
+
+        Returns:
+            Return all apks in <partition>/priv-app
+        Throws:
+            MissingResourceError <partition>/priv-app cannot be found.
+        """
+        if not self.adb.serial:
+            priv_app_dir = os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+                                        partition + '/priv-app')
         else:
             try:
-                system_priv_app_dir = self.adb.pull('/system/priv-app/')
+                priv_app_dir = self.adb.pull(partition + '/priv-app/')
             except subprocess.CalledProcessError:
                 raise MissingResourceError(
-                    'Directory "/system/priv-app" could not be pulled from on '
-                    'device "%s".' % self.adb.serial)
+                    'Directory "%s/priv-app" could not be pulled from on '
+                    'device "%s".' % (partition, self.adb.serial))
+        return get_output('find %s -name "*.apk"' % priv_app_dir).split()
 
-        return get_output('find %s -name "*.apk"' % system_priv_app_dir).split()
-
-    def _resolve_all_product_privapps(self):
-        """Extract package name and requested permissions."""
-        if self._is_android_env:
-            product_priv_app_dir = (
-                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
-                                            'product/priv-app'))
-            if not os.path.exists(product_priv_app_dir):
-                product_priv_app_dir  = (
-                        os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
-                                                'system/product/priv-app'))
-        else:
-            try:
-                product_priv_app_dir = self.adb.pull('/product/priv-app/')
-            except subprocess.CalledProcessError:
-                print('Warning: Directory "/product/priv-app" could not be '
-                        'pulled from on device "%s". Trying '
-                        '"/system/product/priv-app"' % self.adb.serial,
-                        file=sys.stderr)
-                try:
-                    product_priv_app_dir = (
-                            self.adb.pull('/system/product/priv-app/'))
-                except subprocess.CalledProcessError:
-                    raise MissingResourceError(
-                        'Directory "/system/product/priv-app" could not be '
-                        'pulled from on device "%s".' % self.adb.serial)
-
-        return get_output(
-                'find %s -name "*.apk"' % product_priv_app_dir).split()
-
-    def _resolve_sys_path(self, file_path, fallback_file_path=None):
+    def _resolve_sys_path(self, file_path):
         """Resolves a path that is a part of an Android System Image."""
-        if self._is_android_env:
-            sys_path = (
-                    os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path))
-            if not os.path.exists(sys_path):
-                sys_path = (
-                        os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
-                        fallback_file_path))
+        if not self.adb.serial:
+            return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path)
         else:
-            try:
-                sys_path = self.adb.pull(file_path)
-            except subprocess.CalledProcessError:
-                print('Warning: Directory %s could not be pulled from on device'
-                        '"%s". Trying "/system/product/priv-app"'
-                        % (file_path, self.adb.serial), file=sys.stderr)
-                try:
-                    sys_path = self.adb.pull(fallback_file_path)
-                except subprocess.CalledProcessError:
-                    raise MissingResourceError(
-                        'Directory %s could not be pulled from on '
-                        'device "%s".' % (fallback_file_path, self.adb.serial))
+            return self.adb.pull(file_path)
 
-        return sys_path
+    def _resolve_sys_paths(self, file_path, partitions):
+        """Resolves a path that is a part of an Android System Image, for the
+        specified partitions."""
+        results = {}
+        for p in partitions:
+            results[p] = self._resolve_sys_path(p + '/' + file_path)
+        return results
 
 
 def get_output(command):
@@ -416,10 +493,19 @@
              'details.'
     )
     parser.add_argument(
+        '-v',
+        '--verbose',
+        action='store_true',
+        default=False,
+        required=False,
+        help='Whether or not to enable more verbose logs such as '
+             'adb pull progress to be shown'
+    )
+    parser.add_argument(
         '--adb',
         type=str,
         required=False,
-        metavar='<ADB_PATH',
+        metavar='<ADB_PATH>',
         help='Path to adb. If none specified, uses the environment\'s adb.'
     )
     parser.add_argument(
@@ -442,6 +528,19 @@
              'code 1. If -s is given, -d is not needed.'
     )
     parser.add_argument(
+        '-p',
+        '--partitions',
+        type=str,
+        required=False,
+        default='all',
+        metavar='<PARTITION>',
+        help='The target partition(s) to examine permissions for. '
+             'It is set to "all" by default, which means all the partitions '
+             'where priv-app diectory exists will be examined'
+             'Use "," as a delimiter when specifying multiple partitions. '
+             'E.g. "system,product"'
+    )
+    parser.add_argument(
         'apks',
         nargs='*',
         type=str,
@@ -473,25 +572,65 @@
 
     return cmd_args
 
-def create_permission_file(resources, privapp_apks, permissions_dir,
-            sysconfig_dir, file=None):
-    # Parse base XML files in /etc dir, permissions listed there don't have
-    # to be re-added
-    base_permissions = {}
-    base_xml_files = itertools.chain(list_xml_files(permissions_dir),
-                                     list_xml_files(sysconfig_dir))
-    for xml_file in base_xml_files:
-        parse_config_xml(xml_file, base_permissions)
 
+def create_permission_file(resources):
+    """Prints out/creates permission file with missing permissions."""
+    # First extract privileged permissions from framework-res.apk
     priv_permissions = extract_priv_permissions(resources.aapt,
                                                 resources.framework_res_apk)
 
+    results = {}
+    for p in resources.partitions:
+        results[p], apps_redefine_base = \
+            generate_missing_permissions(resources, priv_permissions, p)
+        enable_print = True
+        vprint(enable_print, '#' * 80)
+        vprint(enable_print, '#')
+        if resources.writetodisk:
+            # Check if it is likely a product partition
+            if p.endswith('product'):
+                out_file_name = resources.productfile;
+            # Check if it is a system partition
+            elif p.endswith('system'):
+                out_file_name = resources.systemfile
+            # Fallback to the partition name itself
+            else:
+                out_file_name = str(p).replace('/', '_') + '.xml'
+
+            out_file = open(out_file_name, 'w')
+            vprint(enable_print, '# %s XML written to %s:', p, out_file_name)
+            vprint(enable_print, '#')
+            vprint(enable_print, '#' * 80)
+            print_xml(results[p], apps_redefine_base, p, out_file)
+            out_file.close()
+        else:
+            vprint(enable_print, '# %s XML:', p)
+            vprint(enable_print, '#')
+            vprint(enable_print, '#' * 80)
+
+        # Print it to stdout regardless of whether writing to a file or not
+        print_xml(results[p], apps_redefine_base, p)
+
+
+def generate_missing_permissions(resources, priv_permissions, partition):
+    """Generates the missing permissions for the specified partition."""
+    # Parse base XML files in /etc dir, permissions listed there don't have
+    # to be re-added
+    base_permissions = {}
+    base_xml_files = itertools.chain(
+        list_xml_files(resources.permissions_dirs[partition]),
+        list_xml_files(resources.sysconfig_dirs[partition]))
+
+    for xml_file in base_xml_files:
+        parse_config_xml(xml_file, base_permissions)
+
     apps_redefine_base = []
     results = {}
-    for priv_app in privapp_apks:
+    for priv_app in resources.privapp_apks[partition]:
         pkg_info = extract_pkg_and_requested_permissions(resources.aapt,
                                                          priv_app)
         pkg_name = pkg_info['package_name']
+        # get intersection of what's requested by app and by framework
         priv_perms = get_priv_permissions(pkg_info['permissions'],
                                           priv_permissions)
         # Compute diff against permissions defined in base file
@@ -504,13 +643,14 @@
         if priv_perms:
             results[pkg_name] = sorted(priv_perms)
 
-    print_xml(results, apps_redefine_base)
-    if file is not None:
-        print_xml(results, apps_redefine_base, file)
+    return results, apps_redefine_base
 
-def print_xml(results, apps_redefine_base, fd=sys.stdout):
+
+def print_xml(results, apps_redefine_base, partition, fd=sys.stdout):
     """Print results to the given file."""
-    fd.write('<?xml version="1.0" encoding="utf-8"?>\n<permissions>\n')
+    fd.write('<?xml version="1.0" encoding="utf-8"?>\n')
+    fd.write('<!-- for the partition: /%s -->\n' % partition)
+    fd.write('<permissions>\n')
     for package_name in sorted(results):
         if package_name in apps_redefine_base:
             fd.write('    <!-- Additional permissions on top of %s -->\n' %
@@ -641,7 +781,6 @@
     del temp_dirs[:]
     del temp_files[:]
 
-
 if __name__ == '__main__':
     args = parse_args()
     try:
@@ -650,45 +789,14 @@
             adb_path=args.adb,
             use_device=args.device,
             serial=args.serial,
+            partitions=args.partitions,
+            verbose=args.verbose,
+            writetodisk=args.writetodisk,
+            systemfile=args.systemfile,
+            productfile=args.productfile,
             apks=args.apks
         )
-        system_permission_file=None
-        product_permission_file=None
-        print('#' * 80)
-        print('#')
-        if args.writetodisk:
-            print('#System XML written to %s:' % args.systemfile)
-            system_permission_file = open(args.systemfile, 'w')
-        else:
-            print('#System XML:')
-        print('#')
-        print('#' * 80)
-        create_permission_file(
-            tool_resources,
-            tool_resources.system_privapp_apks,
-            tool_resources.system_permissions_dir,
-            tool_resources.system_sysconfig_dir,
-            system_permission_file)
-        if args.writetodisk:
-            system_permission_file.close()
-        if tool_resources.product_privapp_apks:
-            print('#' * 80)
-            print('#')
-            if args.writetodisk:
-                print('#Product XML written to %s:' % args.productfile)
-                product_permission_file = open(args.productfile, 'w')
-            else:
-                print('#Product XML:')
-            print('#')
-            print('#' * 80)
-            create_permission_file(
-                tool_resources,
-                tool_resources.product_privapp_apks,
-                tool_resources.product_permissions_dir,
-                tool_resources.product_sysconfig_dir,
-                product_permission_file)
-            if args.writetodisk:
-                product_permission_file.close()
+        create_permission_file(tool_resources)
     except MissingResourceError as e:
         print(str(e), file=sys.stderr)
         exit(1)
diff --git a/tools/winscope/.babelrc b/tools/winscope/.babelrc
index 3ed94df..cedf24f 100644
--- a/tools/winscope/.babelrc
+++ b/tools/winscope/.babelrc
@@ -1,5 +1,5 @@
 {
   "presets": [
-    ["env", { "modules": false }]
+    "@babel/preset-env"
   ]
-}
+}
\ No newline at end of file
diff --git a/tools/winscope/.eslintrc.json b/tools/winscope/.eslintrc.json
new file mode 100644
index 0000000..b5271d1
--- /dev/null
+++ b/tools/winscope/.eslintrc.json
@@ -0,0 +1,35 @@
+{
+    "env": {
+        "browser": true,
+        "es6": true
+    },
+    "extends": [
+        "plugin:vue/essential",
+        "google"
+    ],
+    "globals": {
+        "Atomics": "readonly",
+        "SharedArrayBuffer": "readonly"
+    },
+    "parserOptions": {
+        "ecmaVersion": 11,
+        "sourceType": "module"
+    },
+    "plugins": [
+        "vue"
+    ],
+    "rules": {
+        "require-jsdoc": [
+            "error",
+            {
+                "require": {
+                    "FunctionDeclaration": false,
+                    "MethodDefinition": false,
+                    "ClassDeclaration": false,
+                    "ArrowFunctionExpression": false,
+                    "FunctionExpression": false
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tools/winscope/.gitignore b/tools/winscope/.gitignore
index 1eba9d2..75eede8 100644
--- a/tools/winscope/.gitignore
+++ b/tools/winscope/.gitignore
@@ -1,2 +1,8 @@
 node_modules/
 adb_proxy/venv/
+.vscode/
+dist/
+kotlin_build/
+yarn-error.log
+kotlin_build/
+.eslintcache
diff --git a/tools/winscope/README.md b/tools/winscope/README.md
index 8821601..d09563d 100644
--- a/tools/winscope/README.md
+++ b/tools/winscope/README.md
@@ -17,6 +17,29 @@
 * Navigate to `development/tools/winscope`
 * Run `yarn run dev`
 
+### Update IntDefMapping
+* Build `framework-all` module and a preprocessor will generate the latest
+IntDefMapping. From the `ANDROID_ROOT` run:
+```
+. build/envsetup.sh
+m framework-all
+```
+
+* Copy the generated `intDefMapping.json` file to the `prebuilts` repo.
+```
+cp
+./out/soong/.intermediates/frameworks/base/framework-all/android_common/javac/classes/com/android/winscope/intDefMapping.json
+./prebuilts/misc/common/winscope/intDefMapping.json
+```
+
+* Upload the changes.
+```
+cd ./prebuilts/misc/common/winscope
+repo start intdef-update
+git commit -am "Update intdef mapping" "Test: N/A"
+repo upload --cbr .
+```
+
 ### Building with internal extensions
 Internal paths in vendor/ which are not available in AOSP must be replaced by
 stub files. See getWaylandSafePath for an example
diff --git a/tools/winscope/adb_proxy/winscope_proxy.py b/tools/winscope/adb_proxy/winscope_proxy.py
index c799a8d..17500b7 100755
--- a/tools/winscope/adb_proxy/winscope_proxy.py
+++ b/tools/winscope/adb_proxy/winscope_proxy.py
@@ -38,6 +38,7 @@
 from http import HTTPStatus
 from http.server import HTTPServer, BaseHTTPRequestHandler
 from tempfile import NamedTemporaryFile
+import base64
 
 # CONFIG #
 
@@ -46,7 +47,7 @@
 PORT = 5544
 
 # Keep in sync with WINSCOPE_PROXY_VERSION in Winscope DataAdb.vue
-VERSION = '0.5'
+VERSION = '0.8'
 
 WINSCOPE_VERSION_HEADER = "Winscope-Proxy-Version"
 WINSCOPE_TOKEN_HEADER = "Winscope-Token"
@@ -62,47 +63,122 @@
 log = logging.getLogger("ADBProxy")
 
 
+class File:
+    def __init__(self, file, filetype) -> None:
+        self.file = file
+        self.type = filetype
+
+    def get_filepaths(self, device_id):
+        return [self.file]
+
+    def get_filetype(self):
+        return self.type
+
+
+class FileMatcher:
+    def __init__(self, path, matcher, filetype) -> None:
+        self.path = path
+        self.matcher = matcher
+        self.type = filetype
+
+    def get_filepaths(self, device_id):
+        matchingFiles = call_adb(
+            f"shell su root find {self.path} -name {self.matcher}", device_id)
+
+        return matchingFiles.split('\n')[:-1]
+
+    def get_filetype(self):
+        return self.type
+
+
 class TraceTarget:
     """Defines a single parameter to trace.
 
     Attributes:
-        file: the path on the device the trace results are saved to.
+        file_matchers: the matchers used to identify the paths on the device the trace results are saved to.
         trace_start: command to start the trace from adb shell, must not block.
         trace_stop: command to stop the trace, should block until the trace is stopped.
     """
 
-    def __init__(self, file: str, trace_start: str, trace_stop: str) -> None:
-        self.file = file
+    def __init__(self, files, trace_start: str, trace_stop: str) -> None:
+        if type(files) is not list:
+            files = [files]
+        self.files = files
         self.trace_start = trace_start
         self.trace_stop = trace_stop
 
 
+# Order of files matters as they will be expected in that order and decoded in that order
 TRACE_TARGETS = {
     "window_trace": TraceTarget(
-        "/data/misc/wmtrace/wm_trace.pb",
+        File("/data/misc/wmtrace/wm_trace.pb", "window_trace"),
         'su root cmd window tracing start\necho "WM trace started."',
         'su root cmd window tracing stop >/dev/null 2>&1'
     ),
     "layers_trace": TraceTarget(
-        "/data/misc/wmtrace/layers_trace.pb",
+        File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"),
         'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."',
         'su root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1'
     ),
     "screen_recording": TraceTarget(
-        "/data/local/tmp/screen.winscope.mp4",
+        File("/data/local/tmp/screen.winscope.mp4", "screen_recording"),
         'screenrecord --bit-rate 8M /data/local/tmp/screen.winscope.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."',
         'pkill -l SIGINT screenrecord >/dev/null 2>&1'
     ),
     "transaction": TraceTarget(
-        "/data/misc/wmtrace/transaction_trace.pb",
+        [
+            File("/data/misc/wmtrace/transaction_trace.pb", "transactions"),
+            FileMatcher("/data/misc/wmtrace/", "transaction_merges_*.pb",
+                        "transaction_merges"),
+        ],
         'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."',
         'su root service call SurfaceFlinger 1020 i32 0 >/dev/null 2>&1'
     ),
     "proto_log": TraceTarget(
-        "/data/misc/wmtrace/wm_log.pb",
+        File("/data/misc/wmtrace/wm_log.pb", "proto_log"),
         'su root cmd window logging start\necho "WM logging started."',
         'su root cmd window logging stop >/dev/null 2>&1'
     ),
+    "ime_trace_clients": TraceTarget(
+        File("/data/misc/wmtrace/ime_trace_clients.pb", "ime_trace_clients"),
+        'su root ime tracing start\necho "Clients IME trace started."',
+        'su root ime tracing stop >/dev/null 2>&1'
+    ),
+   "ime_trace_service": TraceTarget(
+        File("/data/misc/wmtrace/ime_trace_service.pb", "ime_trace_service"),
+        'su root ime tracing start\necho "Service IME trace started."',
+        'su root ime tracing stop >/dev/null 2>&1'
+    ),
+    "ime_trace_managerservice": TraceTarget(
+        File("/data/misc/wmtrace/ime_trace_managerservice.pb", "ime_trace_managerservice"),
+        'su root ime tracing start\necho "ManagerService IME trace started."',
+        'su root ime tracing stop >/dev/null 2>&1'
+    ),
+}
+
+
+class SurfaceFlingerTraceConfig:
+    """Handles optional configuration for surfaceflinger traces.
+    """
+
+    def __init__(self) -> None:
+        # default config flags
+        self.flags = 1 << 0 | 1 << 1
+
+    def add(self, config: str) -> None:
+        self.flags |= CONFIG_FLAG[config]
+
+    def is_valid(self, config: str) -> bool:
+        return config in CONFIG_FLAG
+
+    def command(self) -> str:
+        return f'su root service call SurfaceFlinger 1033 i32 {self.flags}'
+
+
+CONFIG_FLAG = {
+    "composition": 1 << 2,
+    "metadata": 1 << 3,
+    "hwc": 1 << 4
 }
 
 
@@ -114,18 +190,20 @@
         dump_command: command to dump state to file.
     """
 
-    def __init__(self, file: str, dump_command: str) -> None:
-        self.file = file
+    def __init__(self, files, dump_command: str) -> None:
+        if type(files) is not list:
+            files = [files]
+        self.files = files
         self.dump_command = dump_command
 
 
 DUMP_TARGETS = {
     "window_dump": DumpTarget(
-        "/data/local/tmp/wm_dump.pb",
+        File("/data/local/tmp/wm_dump.pb", "window_dump"),
         'su root dumpsys window --proto > /data/local/tmp/wm_dump.pb'
     ),
     "layers_dump": DumpTarget(
-        "/data/local/tmp/sf_dump.pb",
+        File("/data/local/tmp/sf_dump.pb", "layers_dump"),
         'su root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump.pb'
     )
 }
@@ -139,18 +217,21 @@
     try:
         with open(WINSCOPE_TOKEN_LOCATION, 'r') as token_file:
             token = token_file.readline()
-            log.debug("Loaded token {} from {}".format(token, WINSCOPE_TOKEN_LOCATION))
+            log.debug("Loaded token {} from {}".format(
+                token, WINSCOPE_TOKEN_LOCATION))
             return token
     except IOError:
         token = secrets.token_hex(32)
         os.makedirs(os.path.dirname(WINSCOPE_TOKEN_LOCATION), exist_ok=True)
         try:
             with open(WINSCOPE_TOKEN_LOCATION, 'w') as token_file:
-                log.debug("Created and saved token {} to {}".format(token, WINSCOPE_TOKEN_LOCATION))
+                log.debug("Created and saved token {} to {}".format(
+                    token, WINSCOPE_TOKEN_LOCATION))
                 token_file.write(token)
             os.chmod(WINSCOPE_TOKEN_LOCATION, 0o600)
         except IOError:
-            log.error("Unable to save persistent token {} to {}".format(token, WINSCOPE_TOKEN_LOCATION))
+            log.error("Unable to save persistent token {} to {}".format(
+                token, WINSCOPE_TOKEN_LOCATION))
         return token
 
 
@@ -167,8 +248,10 @@
     server.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
     server.send_header('Access-Control-Allow-Origin', '*')
     server.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
-    server.send_header('Access-Control-Allow-Headers', WINSCOPE_TOKEN_HEADER + ', Content-Type, Content-Length')
-    server.send_header('Access-Control-Expose-Headers', 'Winscope-Proxy-Version')
+    server.send_header('Access-Control-Allow-Headers',
+                       WINSCOPE_TOKEN_HEADER + ', Content-Type, Content-Length')
+    server.send_header('Access-Control-Expose-Headers',
+                       'Winscope-Proxy-Version')
     server.send_header(WINSCOPE_VERSION_HEADER, VERSION)
     server.end_headers()
 
@@ -192,7 +275,7 @@
 
 
 class RequestRouter:
-    """Handles HTTP request authenticationn and routing"""
+    """Handles HTTP request authentication and routing"""
 
     def __init__(self, handler):
         self.request = handler
@@ -208,7 +291,8 @@
 
     def __internal_error(self, error: str):
         log.error("Internal error: " + error)
-        self.request.respond(HTTPStatus.INTERNAL_SERVER_ERROR, error.encode("utf-8"), 'text/txt')
+        self.request.respond(HTTPStatus.INTERNAL_SERVER_ERROR,
+                             error.encode("utf-8"), 'text/txt')
 
     def __bad_token(self):
         log.info("Bad token")
@@ -241,11 +325,15 @@
         log.debug("Call: " + ' '.join(command))
         return subprocess.check_output(command, stderr=subprocess.STDOUT, input=stdin).decode('utf-8')
     except OSError as ex:
-        log.debug('Error executing adb command: {}\n{}'.format(' '.join(command), repr(ex)))
-        raise AdbError('Error executing adb command: {}\n{}'.format(' '.join(command), repr(ex)))
+        log.debug('Error executing adb command: {}\n{}'.format(
+            ' '.join(command), repr(ex)))
+        raise AdbError('Error executing adb command: {}\n{}'.format(
+            ' '.join(command), repr(ex)))
     except subprocess.CalledProcessError as ex:
-        log.debug('Error executing adb command: {}\n{}'.format(' '.join(command), ex.output.decode("utf-8")))
-        raise AdbError('Error executing adb command: adb {}\n{}'.format(params, ex.output.decode("utf-8")))
+        log.debug('Error executing adb command: {}\n{}'.format(
+            ' '.join(command), ex.output.decode("utf-8")))
+        raise AdbError('Error executing adb command: adb {}\n{}'.format(
+            params, ex.output.decode("utf-8")))
 
 
 def call_adb_outfile(params: str, outfile, device: str = None, stdin: bytes = None):
@@ -260,12 +348,14 @@
             raise AdbError('Error executing adb command: adb {}\n'.format(params) + err.decode(
                 'utf-8') + '\n' + outfile.read().decode('utf-8'))
     except OSError as ex:
-        log.debug('Error executing adb command: adb {}\n{}'.format(params, repr(ex)))
-        raise AdbError('Error executing adb command: adb {}\n{}'.format(params, repr(ex)))
+        log.debug('Error executing adb command: adb {}\n{}'.format(
+            params, repr(ex)))
+        raise AdbError(
+            'Error executing adb command: adb {}\n{}'.format(params, repr(ex)))
 
 
 class ListDevicesEndpoint(RequestEndpoint):
-    ADB_INFO_RE = re.compile("^([A-Za-z0-9\\-]+)\\s+(\\w+)(.*model:(\\w+))?")
+    ADB_INFO_RE = re.compile("^([A-Za-z0-9.:\\-]+)\\s+(\\w+)(.*model:(\\w+))?")
 
     def process(self, server, path):
         lines = list(filter(None, call_adb('devices -l').split('\n')))
@@ -280,7 +370,7 @@
 
 class DeviceRequestEndpoint(RequestEndpoint):
     def process(self, server, path):
-        if len(path) > 0 and re.fullmatch("[A-Za-z0-9\\-]+", path[0]):
+        if len(path) > 0 and re.fullmatch("[A-Za-z0-9.:\\-]+", path[0]):
             self.process_with_device(server, path[1:], path[0])
         else:
             raise BadRequest("Device id not specified")
@@ -289,34 +379,51 @@
     def process_with_device(self, server, path, device_id):
         pass
 
+    def get_request(self, server) -> str:
+        try:
+            length = int(server.headers["Content-Length"])
+        except KeyError as err:
+            raise BadRequest("Missing Content-Length header\n" + str(err))
+        except ValueError as err:
+            raise BadRequest("Content length unreadable\n" + str(err))
+        return json.loads(server.rfile.read(length).decode("utf-8"))
 
-class FetchFileEndpoint(DeviceRequestEndpoint):
+
+class FetchFilesEndpoint(DeviceRequestEndpoint):
     def process_with_device(self, server, path, device_id):
         if len(path) != 1:
             raise BadRequest("File not specified")
         if path[0] in TRACE_TARGETS:
-            file_path = TRACE_TARGETS[path[0]].file
+            files = TRACE_TARGETS[path[0]].files
         elif path[0] in DUMP_TARGETS:
-            file_path = DUMP_TARGETS[path[0]].file
+            files = DUMP_TARGETS[path[0]].files
         else:
             raise BadRequest("Unknown file specified")
 
-        with NamedTemporaryFile() as tmp:
-            log.debug("Fetching file {} from device to {}".format(file_path, tmp.name))
-            call_adb_outfile('exec-out su root cat ' + file_path, tmp, device_id)
-            log.debug("Deleting file {} from device".format(file_path))
-            call_adb('shell su root rm ' + file_path, device_id)
-            server.send_response(HTTPStatus.OK)
-            server.send_header('X-Content-Type-Options', 'nosniff')
-            server.send_header('Content-type', 'application/octet-stream')
-            add_standard_headers(server)
-            log.debug("Uploading file {}".format(tmp.name))
-            while True:
-                buf = tmp.read(1024)
-                if buf:
-                    server.wfile.write(buf)
-                else:
-                    break
+        file_buffers = dict()
+
+        for f in files:
+            file_type = f.get_filetype()
+            file_paths = f.get_filepaths(device_id)
+
+            for file_path in file_paths:
+                with NamedTemporaryFile() as tmp:
+                    log.debug(
+                        f"Fetching file {file_path} from device to {tmp.name}")
+                    call_adb_outfile('exec-out su root cat ' +
+                                     file_path, tmp, device_id)
+                    log.debug(f"Deleting file {file_path} from device")
+                    call_adb('shell su root rm ' + file_path, device_id)
+                    log.debug(f"Uploading file {tmp.name}")
+                    if file_type not in file_buffers:
+                        file_buffers[file_type] = []
+                    buf = base64.encodebytes(tmp.read()).decode("utf-8")
+                    file_buffers[file_type].append(buf)
+
+        # server.send_header('X-Content-Type-Options', 'nosniff')
+        # add_standard_headers(server)
+        j = json.dumps(file_buffers)
+        server.respond(HTTPStatus.OK, j.encode("utf-8"), "text/json")
 
 
 def check_root(device_id):
@@ -341,34 +448,41 @@
             self.process = subprocess.Popen(shell, stdout=subprocess.PIPE,
                                             stderr=subprocess.PIPE, stdin=subprocess.PIPE, start_new_session=True)
         except OSError as ex:
-            raise AdbError('Error executing adb command: adb shell\n{}'.format(repr(ex)))
+            raise AdbError(
+                'Error executing adb command: adb shell\n{}'.format(repr(ex)))
 
         super().__init__()
 
     def timeout(self):
         if self.is_alive():
-            log.warning("Keep-alive timeout for trace on {}".format(self._device_id))
+            log.warning(
+                "Keep-alive timeout for trace on {}".format(self._device_id))
             self.end_trace()
             if self._device_id in TRACE_THREADS:
                 TRACE_THREADS.pop(self._device_id)
 
     def reset_timer(self):
-        log.debug("Resetting keep-alive clock for trace on {}".format(self._device_id))
+        log.debug(
+            "Resetting keep-alive clock for trace on {}".format(self._device_id))
         if self._keep_alive_timer:
             self._keep_alive_timer.cancel()
-        self._keep_alive_timer = threading.Timer(KEEP_ALIVE_INTERVAL_S, self.timeout)
+        self._keep_alive_timer = threading.Timer(
+            KEEP_ALIVE_INTERVAL_S, self.timeout)
         self._keep_alive_timer.start()
 
     def end_trace(self):
         if self._keep_alive_timer:
             self._keep_alive_timer.cancel()
-        log.debug("Sending SIGINT to the trace process on {}".format(self._device_id))
+        log.debug("Sending SIGINT to the trace process on {}".format(
+            self._device_id))
         self.process.send_signal(signal.SIGINT)
         try:
-            log.debug("Waiting for trace shell to exit for {}".format(self._device_id))
+            log.debug("Waiting for trace shell to exit for {}".format(
+                self._device_id))
             self.process.wait(timeout=5)
         except TimeoutError:
-            log.debug("TIMEOUT - sending SIGKILL to the trace process on {}".format(self._device_id))
+            log.debug(
+                "TIMEOUT - sending SIGKILL to the trace process on {}".format(self._device_id))
             self.process.kill()
         self.join()
 
@@ -380,8 +494,10 @@
         time.sleep(0.2)
         for i in range(10):
             if call_adb("shell su root cat /data/local/tmp/winscope_status", device=self._device_id) == 'TRACE_OK\n':
-                call_adb("shell su root rm /data/local/tmp/winscope_status", device=self._device_id)
-                log.debug("Trace finished successfully on {}".format(self._device_id))
+                call_adb(
+                    "shell su root rm /data/local/tmp/winscope_status", device=self._device_id)
+                log.debug("Trace finished successfully on {}".format(
+                    self._device_id))
                 self._success = True
                 break
             log.debug("Still waiting for cleanup on {}".format(self._device_id))
@@ -419,13 +535,7 @@
 
     def process_with_device(self, server, path, device_id):
         try:
-            length = int(server.headers["Content-Length"])
-        except KeyError as err:
-            raise BadRequest("Missing Content-Length header\n" + str(err))
-        except ValueError as err:
-            raise BadRequest("Content length unreadable\n" + str(err))
-        try:
-            requested_types = json.loads(server.rfile.read(length).decode("utf-8"))
+            requested_types = self.get_request(server)
             requested_traces = [TRACE_TARGETS[t] for t in requested_types]
         except KeyError as err:
             raise BadRequest("Unsupported trace target\n" + str(err))
@@ -439,8 +549,10 @@
         command = StartTrace.TRACE_COMMAND.format(
             '\n'.join([t.trace_stop for t in requested_traces]),
             '\n'.join([t.trace_start for t in requested_traces]))
-        log.debug("Trace requested for {} with targets {}".format(device_id, ','.join(requested_types)))
-        TRACE_THREADS[device_id] = TraceThread(device_id, command.encode('utf-8'))
+        log.debug("Trace requested for {} with targets {}".format(
+            device_id, ','.join(requested_types)))
+        TRACE_THREADS[device_id] = TraceThread(
+            device_id, command.encode('utf-8'))
         TRACE_THREADS[device_id].start()
         server.respond(HTTPStatus.OK, b'', "text/plain")
 
@@ -453,7 +565,8 @@
             TRACE_THREADS[device_id].end_trace()
 
         success = TRACE_THREADS[device_id].success()
-        out = TRACE_THREADS[device_id].out + b"\n" + TRACE_THREADS[device_id].err
+        out = TRACE_THREADS[device_id].out + \
+            b"\n" + TRACE_THREADS[device_id].err
         command = TRACE_THREADS[device_id].trace_command
         TRACE_THREADS.pop(device_id)
         if success:
@@ -465,24 +578,50 @@
                     "utf-8"))
 
 
+class ConfigTrace(DeviceRequestEndpoint):
+    def process_with_device(self, server, path, device_id):
+        try:
+            requested_configs = self.get_request(server)
+            config = SurfaceFlingerTraceConfig()
+            for requested_config in requested_configs:
+                if not config.is_valid(requested_config):
+                    raise BadRequest(
+                        f"Unsupported config {requested_config}\n")
+                config.add(requested_config)
+        except KeyError as err:
+            raise BadRequest("Unsupported trace target\n" + str(err))
+        if device_id in TRACE_THREADS:
+            BadRequest(f"Trace in progress for {device_id}")
+        if not check_root(device_id):
+            raise AdbError(
+                f"Unable to acquire root privileges on the device - check the output of 'adb -s {device_id} shell su root id'")
+        command = config.command()
+        shell = ['adb', '-s', device_id, 'shell']
+        log.debug(f"Starting shell {' '.join(shell)}")
+        process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                   stdin=subprocess.PIPE, start_new_session=True)
+        log.debug(f"Changing trace config on device {device_id}")
+        out, err = process.communicate(command.encode('utf-8'))
+        if process.returncode != 0:
+            raise AdbError(
+                f"Error executing command:\n {command}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}")
+        log.debug(f"Changing trace config finished on device {device_id}")
+        server.respond(HTTPStatus.OK, b'', "text/plain")
+
+
 class StatusEndpoint(DeviceRequestEndpoint):
     def process_with_device(self, server, path, device_id):
         if device_id not in TRACE_THREADS:
             raise BadRequest("No trace in progress for {}".format(device_id))
         TRACE_THREADS[device_id].reset_timer()
-        server.respond(HTTPStatus.OK, str(TRACE_THREADS[device_id].is_alive()).encode("utf-8"), "text/plain")
+        server.respond(HTTPStatus.OK, str(
+            TRACE_THREADS[device_id].is_alive()).encode("utf-8"), "text/plain")
 
 
 class DumpEndpoint(DeviceRequestEndpoint):
     def process_with_device(self, server, path, device_id):
         try:
-            length = int(server.headers["Content-Length"])
-        except KeyError as err:
-            raise BadRequest("Missing Content-Length header\n" + str(err))
-        except ValueError as err:
-            raise BadRequest("Content length unreadable\n" + str(err))
-        try:
-            requested_types = json.loads(server.rfile.read(length).decode("utf-8"))
+            requested_types = self.get_request(server)
             requested_traces = [DUMP_TARGETS[t] for t in requested_types]
         except KeyError as err:
             raise BadRequest("Unsupported trace target\n" + str(err))
@@ -491,7 +630,7 @@
         if not check_root(device_id):
             raise AdbError(
                 "Unable to acquire root privileges on the device - check the output of 'adb -s {} shell su root id'"
-                    .format(device_id))
+                .format(device_id))
         command = '\n'.join(t.dump_command for t in requested_traces)
         shell = ['adb', '-s', device_id, 'shell']
         log.debug("Starting dump shell {}".format(' '.join(shell)))
@@ -509,12 +648,17 @@
 class ADBWinscopeProxy(BaseHTTPRequestHandler):
     def __init__(self, request, client_address, server):
         self.router = RequestRouter(self)
-        self.router.register_endpoint(RequestType.GET, "devices", ListDevicesEndpoint())
-        self.router.register_endpoint(RequestType.GET, "status", StatusEndpoint())
-        self.router.register_endpoint(RequestType.GET, "fetch", FetchFileEndpoint())
+        self.router.register_endpoint(
+            RequestType.GET, "devices", ListDevicesEndpoint())
+        self.router.register_endpoint(
+            RequestType.GET, "status", StatusEndpoint())
+        self.router.register_endpoint(
+            RequestType.GET, "fetch", FetchFilesEndpoint())
         self.router.register_endpoint(RequestType.POST, "start", StartTrace())
         self.router.register_endpoint(RequestType.POST, "end", EndTrace())
         self.router.register_endpoint(RequestType.POST, "dump", DumpEndpoint())
+        self.router.register_endpoint(
+            RequestType.POST, "configtrace", ConfigTrace())
         super().__init__(request, client_address, server)
 
     def respond(self, code: int, data: bytes, mime: str) -> None:
diff --git a/tools/winscope/env/dev.env.js b/tools/winscope/env/dev.env.js
new file mode 100644
index 0000000..0b533b2
--- /dev/null
+++ b/tools/winscope/env/dev.env.js
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module.exports = {
+  NODE_ENV: 'development'
+};
\ No newline at end of file
diff --git a/tools/winscope/env/prod.env.js b/tools/winscope/env/prod.env.js
new file mode 100644
index 0000000..f1bbba0
--- /dev/null
+++ b/tools/winscope/env/prod.env.js
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module.exports = {
+  NODE_ENV: 'production'
+};
\ No newline at end of file
diff --git a/tools/winscope/libs/virtualList/Item.js b/tools/winscope/libs/virtualList/Item.js
new file mode 100644
index 0000000..2f1a0e5
--- /dev/null
+++ b/tools/winscope/libs/virtualList/Item.js
@@ -0,0 +1,81 @@
+import Vue from 'vue'
+import { ItemProps, SlotProps } from './props'
+
+const Wrapper = {
+  created() {
+    this.shapeKey = this.horizontal ? 'offsetWidth' : 'offsetHeight'
+  },
+
+  mounted() {
+    if (typeof ResizeObserver !== 'undefined') {
+      this.resizeObserver = new ResizeObserver(() => {
+        this.dispatchSizeChange()
+      })
+      this.resizeObserver.observe(this.$el)
+    }
+  },
+
+  // since component will be reused, so dispatch when updated
+  updated() {
+    this.dispatchSizeChange()
+  },
+
+  beforeDestroy() {
+    if (this.resizeObserver) {
+      this.resizeObserver.disconnect()
+      this.resizeObserver = null
+    }
+  },
+
+  methods: {
+    getCurrentSize() {
+      return this.$el ? this.$el[this.shapeKey] : 0
+    },
+
+    // tell parent current size identify by unique key
+    dispatchSizeChange() {
+      this.$parent.$emit(this.event, this.uniqueKey, this.getCurrentSize(), this.hasInitial)
+    }
+  }
+}
+
+// wrapping for item
+export const Item = Vue.component('virtual-list-item', {
+  mixins: [Wrapper],
+
+  props: ItemProps,
+
+  render(h) {
+    const { tag, component, extraProps = {}, index, scopedSlots = {}, uniqueKey } = this
+    extraProps.source = this.source
+    extraProps.index = index
+
+    return h(tag, {
+      key: uniqueKey,
+      attrs: {
+        role: 'item'
+      }
+    }, [h(component, {
+      props: extraProps,
+      scopedSlots: scopedSlots
+    })])
+  }
+})
+
+// wrapping for slot
+export const Slot = Vue.component('virtual-list-slot', {
+  mixins: [Wrapper],
+
+  props: SlotProps,
+
+  render(h) {
+    const { tag, uniqueKey } = this
+
+    return h(tag, {
+      key: uniqueKey,
+      attrs: {
+        role: uniqueKey
+      }
+    }, this.$slots.default)
+  }
+})
\ No newline at end of file
diff --git a/tools/winscope/libs/virtualList/README.md b/tools/winscope/libs/virtualList/README.md
new file mode 100644
index 0000000..0b99a70
--- /dev/null
+++ b/tools/winscope/libs/virtualList/README.md
@@ -0,0 +1,3 @@
+# vue-virtual-scroll-list
+
+The original library can be found on GitHub at [https://github.com/tangbc/vue-virtual-scroll-list](https://github.com/tangbc/vue-virtual-scroll-list).
\ No newline at end of file
diff --git a/tools/winscope/libs/virtualList/VirtualList.js b/tools/winscope/libs/virtualList/VirtualList.js
new file mode 100644
index 0000000..1a6b632
--- /dev/null
+++ b/tools/winscope/libs/virtualList/VirtualList.js
@@ -0,0 +1,354 @@
+import Vue from 'vue'
+import Virtual from './virtual'
+import { Item, Slot } from './Item'
+import { VirtualProps } from './props'
+
+const EVENT_TYPE = {
+  ITEM: 'item_resize',
+  SLOT: 'slot_resize'
+}
+const SLOT_TYPE = {
+  HEADER: 'header', // string value also use for aria role attribute
+  FOOTER: 'footer'
+}
+
+const VirtualList = Vue.component('virtual-list', {
+  props: VirtualProps,
+
+  data() {
+    return {
+      range: null
+    }
+  },
+
+  watch: {
+    'dataSources.length'() {
+      this.virtual.updateParam('uniqueIds', this.getUniqueIdFromDataSources())
+      this.virtual.handleDataSourcesChange()
+    },
+
+    start(newValue) {
+      this.scrollToIndex(newValue)
+    },
+
+    offset(newValue) {
+      this.scrollToOffset(newValue)
+    }
+  },
+
+  created() {
+    this.isHorizontal = this.direction === 'horizontal'
+    this.directionKey = this.isHorizontal ? 'scrollLeft' : 'scrollTop'
+
+    this.installVirtual()
+
+    // listen item size change
+    this.$on(EVENT_TYPE.ITEM, this.onItemResized)
+
+    // listen slot size change
+    if (this.$slots.header || this.$slots.footer) {
+      this.$on(EVENT_TYPE.SLOT, this.onSlotResized)
+    }
+  },
+
+  // set back offset when awake from keep-alive
+  activated() {
+    this.scrollToOffset(this.virtual.offset)
+  },
+
+  mounted() {
+    // set position
+    if (this.start) {
+      this.scrollToIndex(this.start)
+    } else if (this.offset) {
+      this.scrollToOffset(this.offset)
+    }
+
+    // in page mode we bind scroll event to document
+    if (this.pageMode) {
+      this.updatePageModeFront()
+
+      document.addEventListener('scroll', this.onScroll, {
+        passive: false
+      })
+    }
+  },
+
+  beforeDestroy() {
+    this.virtual.destroy()
+    if (this.pageMode) {
+      document.removeEventListener('scroll', this.onScroll)
+    }
+  },
+
+  methods: {
+    // get item size by id
+    getSize(id) {
+      return this.virtual.sizes.get(id)
+    },
+
+    // get the total number of stored (rendered) items
+    getSizes() {
+      return this.virtual.sizes.size
+    },
+
+    // return current scroll offset
+    getOffset() {
+      if (this.pageMode) {
+        return document.documentElement[this.directionKey] || document.body[this.directionKey]
+      } else {
+        const { root } = this.$refs
+        return root ? Math.ceil(root[this.directionKey]) : 0
+      }
+    },
+
+    // return client viewport size
+    getClientSize() {
+      const key = this.isHorizontal ? 'clientWidth' : 'clientHeight'
+      if (this.pageMode) {
+        return document.documentElement[key] || document.body[key]
+      } else {
+        const { root } = this.$refs
+        return root ? Math.ceil(root[key]) : 0
+      }
+    },
+
+    // return all scroll size
+    getScrollSize() {
+      const key = this.isHorizontal ? 'scrollWidth' : 'scrollHeight'
+      if (this.pageMode) {
+        return document.documentElement[key] || document.body[key]
+      } else {
+        const { root } = this.$refs
+        return root ? Math.ceil(root[key]) : 0
+      }
+    },
+
+    // set current scroll position to a expectant offset
+    scrollToOffset(offset) {
+      if (this.pageMode) {
+        document.body[this.directionKey] = offset
+        document.documentElement[this.directionKey] = offset
+      } else {
+        const { root } = this.$refs
+        if (root) {
+          root[this.directionKey] = offset
+        }
+      }
+    },
+
+    // set current scroll position to a expectant index
+    scrollToIndex(index) {
+      // scroll to bottom
+      if (index >= this.dataSources.length - 1) {
+        this.scrollToBottom()
+      } else {
+        const offset = this.virtual.getOffset(index)
+        this.scrollToOffset(offset)
+      }
+    },
+
+    // set current scroll position to bottom
+    scrollToBottom() {
+      const { shepherd } = this.$refs
+      if (shepherd) {
+        const offset = shepherd[this.isHorizontal ? 'offsetLeft' : 'offsetTop']
+        this.scrollToOffset(offset)
+
+        // check if it's really scrolled to the bottom
+        // maybe list doesn't render and calculate to last range
+        // so we need retry in next event loop until it really at bottom
+        setTimeout(() => {
+          if (this.getOffset() + this.getClientSize() < this.getScrollSize()) {
+            this.scrollToBottom()
+          }
+        }, 3)
+      }
+    },
+
+    // when using page mode we need update slot header size manually
+    // taking root offset relative to the browser as slot header size
+    updatePageModeFront() {
+      const { root } = this.$refs
+      if (root) {
+        const rect = root.getBoundingClientRect()
+        const { defaultView } = root.ownerDocument
+        const offsetFront = this.isHorizontal ? (rect.left + defaultView.pageXOffset) : (rect.top + defaultView.pageYOffset)
+        this.virtual.updateParam('slotHeaderSize', offsetFront)
+      }
+    },
+
+    // reset all state back to initial
+    reset() {
+      this.virtual.destroy()
+      this.scrollToOffset(0)
+      this.installVirtual()
+    },
+
+    // ----------- public method end -----------
+
+    installVirtual() {
+      this.virtual = new Virtual({
+        slotHeaderSize: 0,
+        slotFooterSize: 0,
+        keeps: this.keeps,
+        estimateSize: this.estimateSize,
+        buffer: Math.round(this.keeps / 3), // recommend for a third of keeps
+        uniqueIds: this.getUniqueIdFromDataSources()
+      }, this.onRangeChanged)
+
+      // sync initial range
+      this.range = this.virtual.getRange()
+    },
+
+    getUniqueIdFromDataSources() {
+      const { dataKey } = this
+      return this.dataSources.map((dataSource) => typeof dataKey === 'function' ? dataKey(dataSource) : dataSource[dataKey])
+    },
+
+    // event called when each item mounted or size changed
+    onItemResized(id, size) {
+      this.virtual.saveSize(id, size)
+      this.$emit('resized', id, size)
+    },
+
+    // event called when slot mounted or size changed
+    onSlotResized(type, size, hasInit) {
+      if (type === SLOT_TYPE.HEADER) {
+        this.virtual.updateParam('slotHeaderSize', size)
+      } else if (type === SLOT_TYPE.FOOTER) {
+        this.virtual.updateParam('slotFooterSize', size)
+      }
+
+      if (hasInit) {
+        this.virtual.handleSlotSizeChange()
+      }
+    },
+
+    // here is the re-rendering entry
+    onRangeChanged(range) {
+      this.range = range
+    },
+
+    onScroll(evt) {
+      const offset = this.getOffset()
+      const clientSize = this.getClientSize()
+      const scrollSize = this.getScrollSize()
+
+      // iOS scroll-spring-back behavior will make direction mistake
+      if (offset < 0 || (offset + clientSize > scrollSize + 1) || !scrollSize) {
+        return
+      }
+
+      this.virtual.handleScroll(offset)
+      this.emitEvent(offset, clientSize, scrollSize, evt)
+    },
+
+    // emit event in special position
+    emitEvent(offset, clientSize, scrollSize, evt) {
+      this.$emit('scroll', evt, this.virtual.getRange())
+
+      if (this.virtual.isFront() && !!this.dataSources.length && (offset - this.topThreshold <= 0)) {
+        this.$emit('totop')
+      } else if (this.virtual.isBehind() && (offset + clientSize + this.bottomThreshold >= scrollSize)) {
+        this.$emit('tobottom')
+      }
+    },
+
+    // get the real render slots based on range data
+    // in-place patch strategy will try to reuse components as possible
+    // so those components that are reused will not trigger lifecycle mounted
+    getRenderSlots(h) {
+      const slots = []
+      const { start, end } = this.range
+      const { dataSources, dataKey, itemClass, itemTag, itemStyle, isHorizontal, extraProps, dataComponent, itemScopedSlots } = this
+      for (let index = start; index <= end; index++) {
+        const dataSource = dataSources[index]
+        if (dataSource) {
+          const uniqueKey = typeof dataKey === 'function' ? dataKey(dataSource) : dataSource[dataKey]
+          if (typeof uniqueKey === 'string' || typeof uniqueKey === 'number') {
+            slots.push(h(Item, {
+              props: {
+                index,
+                tag: itemTag,
+                event: EVENT_TYPE.ITEM,
+                horizontal: isHorizontal,
+                uniqueKey: uniqueKey,
+                source: dataSource,
+                extraProps: extraProps,
+                component: dataComponent,
+                scopedSlots: itemScopedSlots
+              },
+              style: itemStyle,
+              class: `${itemClass}${this.itemClassAdd ? ' ' + this.itemClassAdd(index) : ''}`
+            }))
+          } else {
+            console.warn(`Cannot get the data-key '${dataKey}' from data-sources.`)
+          }
+        } else {
+          console.warn(`Cannot get the index '${index}' from data-sources.`)
+        }
+      }
+      return slots
+    }
+  },
+
+  // render function, a closer-to-the-compiler alternative to templates
+  // https://vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth
+  render(h) {
+    const { header, footer } = this.$slots
+    const { padFront, padBehind } = this.range
+    const { isHorizontal, pageMode, rootTag, wrapTag, wrapClass, wrapStyle, headerTag, headerClass, headerStyle, footerTag, footerClass, footerStyle } = this
+    const paddingStyle = { padding: isHorizontal ? `0px ${padBehind}px 0px ${padFront}px` : `${padFront}px 0px ${padBehind}px` }
+    const wrapperStyle = wrapStyle ? Object.assign({}, wrapStyle, paddingStyle) : paddingStyle
+
+    return h(rootTag, {
+      ref: 'root',
+      on: {
+        '&scroll': !pageMode && this.onScroll
+      }
+    }, [
+      // header slot
+      header ? h(Slot, {
+        class: headerClass,
+        style: headerStyle,
+        props: {
+          tag: headerTag,
+          event: EVENT_TYPE.SLOT,
+          uniqueKey: SLOT_TYPE.HEADER
+        }
+      }, header) : null,
+
+      // main list
+      h(wrapTag, {
+        class: wrapClass,
+        attrs: {
+          role: 'group'
+        },
+        style: wrapperStyle
+      }, this.getRenderSlots(h)),
+
+      // footer slot
+      footer ? h(Slot, {
+        class: footerClass,
+        style: footerStyle,
+        props: {
+          tag: footerTag,
+          event: EVENT_TYPE.SLOT,
+          uniqueKey: SLOT_TYPE.FOOTER
+        }
+      }, footer) : null,
+
+      // an empty element use to scroll to bottom
+      h('div', {
+        ref: 'shepherd',
+        style: {
+          width: isHorizontal ? '0px' : '100%',
+          height: isHorizontal ? '100%' : '0px'
+        }
+      })
+    ])
+  }
+})
+
+export default VirtualList
\ No newline at end of file
diff --git a/tools/winscope/libs/virtualList/props.js b/tools/winscope/libs/virtualList/props.js
new file mode 100644
index 0000000..410b581
--- /dev/null
+++ b/tools/winscope/libs/virtualList/props.js
@@ -0,0 +1,150 @@
+export const VirtualProps = {
+    dataKey: {
+        type: [String, Function],
+        required: true
+    },
+    dataSources: {
+        type: Array,
+        required: true
+    },
+    dataComponent: {
+        type: [Object, Function],
+        required: true
+    },
+
+    keeps: {
+        type: Number,
+        default: 30
+    },
+    extraProps: {
+        type: Object
+    },
+    estimateSize: {
+        type: Number,
+        default: 50
+    },
+
+    direction: {
+        type: String,
+        default: 'vertical' // the other value is horizontal
+    },
+    start: {
+        type: Number,
+        default: 0
+    },
+    offset: {
+        type: Number,
+        default: 0
+    },
+    topThreshold: {
+        type: Number,
+        default: 0
+    },
+    bottomThreshold: {
+        type: Number,
+        default: 0
+    },
+    pageMode: {
+        type: Boolean,
+        default: false
+    },
+    rootTag: {
+        type: String,
+        default: 'div'
+    },
+    wrapTag: {
+        type: String,
+        default: 'div'
+    },
+    wrapClass: {
+        type: String,
+        default: ''
+    },
+    wrapStyle: {
+        type: Object
+    },
+    itemTag: {
+        type: String,
+        default: 'div'
+    },
+    itemClass: {
+        type: String,
+        default: ''
+    },
+    itemClassAdd: {
+        type: Function
+    },
+    itemStyle: {
+        type: Object
+    },
+    headerTag: {
+        type: String,
+        default: 'div'
+    },
+    headerClass: {
+        type: String,
+        default: ''
+    },
+    headerStyle: {
+        type: Object
+    },
+    footerTag: {
+        type: String,
+        default: 'div'
+    },
+    footerClass: {
+        type: String,
+        default: ''
+    },
+    footerStyle: {
+        type: Object
+    },
+    itemScopedSlots: {
+        type: Object
+    }
+}
+
+export const ItemProps = {
+    index: {
+        type: Number
+    },
+    event: {
+        type: String
+    },
+    tag: {
+        type: String
+    },
+    horizontal: {
+        type: Boolean
+    },
+    source: {
+        type: Object
+    },
+    component: {
+        type: [Object, Function]
+    },
+    uniqueKey: {
+        type: [String, Number]
+    },
+    extraProps: {
+        type: Object
+    },
+    scopedSlots: {
+        type: Object
+    }
+}
+
+export const SlotProps = {
+    event: {
+        type: String
+    },
+    uniqueKey: {
+        type: String
+    },
+    tag: {
+        type: String
+    },
+    horizontal: {
+        type: Boolean
+    }
+}
\ No newline at end of file
diff --git a/tools/winscope/libs/virtualList/virtual.js b/tools/winscope/libs/virtualList/virtual.js
new file mode 100644
index 0000000..94688f5
--- /dev/null
+++ b/tools/winscope/libs/virtualList/virtual.js
@@ -0,0 +1,305 @@
+const DIRECTION_TYPE = {
+  FRONT: 'FRONT', // scroll up or left
+  BEHIND: 'BEHIND' // scroll down or right
+}
+const CALC_TYPE = {
+  INIT: 'INIT',
+  FIXED: 'FIXED',
+  DYNAMIC: 'DYNAMIC'
+}
+const LEADING_BUFFER = 2
+
+export default class Virtual {
+  constructor(param, callUpdate) {
+    this.init(param, callUpdate)
+  }
+
+  init(param, callUpdate) {
+    // param data
+    this.param = param
+    this.callUpdate = callUpdate
+
+    // size data
+    this.sizes = new Map()
+    this.firstRangeTotalSize = 0
+    this.firstRangeAverageSize = 0
+    this.lastCalcIndex = 0
+    this.fixedSizeValue = 0
+    this.calcType = CALC_TYPE.INIT
+
+    // scroll data
+    this.offset = 0
+    this.direction = ''
+
+    // range data
+    this.range = Object.create(null)
+    if (param) {
+      this.checkRange(0, param.keeps - 1)
+    }
+
+    // benchmark test data
+    // this.__bsearchCalls = 0
+    // this.__getIndexOffsetCalls = 0
+  }
+
+  destroy() {
+    this.init(null, null)
+  }
+
+  // return current render range
+  getRange() {
+    const range = Object.create(null)
+    range.start = this.range.start
+    range.end = this.range.end
+    range.padFront = this.range.padFront
+    range.padBehind = this.range.padBehind
+    return range
+  }
+
+  isBehind() {
+    return this.direction === DIRECTION_TYPE.BEHIND
+  }
+
+  isFront() {
+    return this.direction === DIRECTION_TYPE.FRONT
+  }
+
+  // return start index offset
+  getOffset(start) {
+    return (start < 1 ? 0 : this.getIndexOffset(start)) + this.param.slotHeaderSize
+  }
+
+  updateParam(key, value) {
+    if (this.param && (key in this.param)) {
+      // if uniqueIds change, find out deleted id and remove from size map
+      if (key === 'uniqueIds') {
+        this.sizes.forEach((v, key) => {
+          if (!value.includes(key)) {
+            this.sizes.delete(key)
+          }
+        })
+      }
+      this.param[key] = value
+    }
+  }
+
+  // save each size map by id
+  saveSize(id, size) {
+    this.sizes.set(id, size)
+
+    // we assume size type is fixed at the beginning and remember first size value
+    // if there is no size value different from this at next coming saving
+    // we think it's a fixed size list, otherwise is dynamic size list
+    if (this.calcType === CALC_TYPE.INIT) {
+      this.fixedSizeValue = size
+      this.calcType = CALC_TYPE.FIXED
+    } else if (this.calcType === CALC_TYPE.FIXED && this.fixedSizeValue !== size) {
+      this.calcType = CALC_TYPE.DYNAMIC
+      // it's no use at all
+      delete this.fixedSizeValue
+    }
+
+    // calculate the average size only in the first range
+    if (this.calcType !== CALC_TYPE.FIXED && typeof this.firstRangeTotalSize !== 'undefined') {
+      if (this.sizes.size < Math.min(this.param.keeps, this.param.uniqueIds.length)) {
+        this.firstRangeTotalSize = this.firstRangeTotalSize + size
+        this.firstRangeAverageSize = Math.round(this.firstRangeTotalSize / this.sizes.size)
+      } else {
+        // it's done using
+        delete this.firstRangeTotalSize
+      }
+    }
+  }
+
+  // in some special situation (e.g. length change) we need to update in a row
+  // try going to render next range by a leading buffer according to current direction
+  handleDataSourcesChange() {
+    let start = this.range.start
+
+    if (this.isFront()) {
+      start = start - LEADING_BUFFER
+    } else if (this.isBehind()) {
+      start = start + LEADING_BUFFER
+    }
+
+    start = Math.max(start, 0)
+
+    this.updateRange(this.range.start, this.getEndByStart(start))
+  }
+
+  // when slot size change, we also need force update
+  handleSlotSizeChange() {
+    this.handleDataSourcesChange()
+  }
+
+  // calculating range on scroll
+  handleScroll(offset) {
+    this.direction = offset < this.offset ? DIRECTION_TYPE.FRONT : DIRECTION_TYPE.BEHIND
+    this.offset = offset
+
+    if (this.direction === DIRECTION_TYPE.FRONT) {
+      this.handleFront()
+    } else if (this.direction === DIRECTION_TYPE.BEHIND) {
+      this.handleBehind()
+    }
+  }
+
+  // ----------- public method end -----------
+
+  handleFront() {
+    const overs = this.getScrollOvers()
+    // should not change range if start doesn't exceed overs
+    if (overs > this.range.start) {
+      return
+    }
+
+    // move up start by a buffer length, and make sure its safety
+    const start = Math.max(overs - this.param.buffer, 0)
+    this.checkRange(start, this.getEndByStart(start))
+  }
+
+  handleBehind() {
+    const overs = this.getScrollOvers()
+    // range should not change if scroll overs within buffer
+    if (overs < this.range.start + this.param.buffer) {
+      return
+    }
+
+    this.checkRange(overs, this.getEndByStart(overs))
+  }
+
+  // return the pass overs according to current scroll offset
+  getScrollOvers() {
+    // if slot header exist, we need subtract its size
+    const offset = this.offset - this.param.slotHeaderSize
+    if (offset <= 0) {
+      return 0
+    }
+
+    // if is fixed type, that can be easily
+    if (this.isFixedType()) {
+      return Math.floor(offset / this.fixedSizeValue)
+    }
+
+    let low = 0
+    let middle = 0
+    let middleOffset = 0
+    let high = this.param.uniqueIds.length
+
+    while (low <= high) {
+      // this.__bsearchCalls++
+      middle = low + Math.floor((high - low) / 2)
+      middleOffset = this.getIndexOffset(middle)
+
+      if (middleOffset === offset) {
+        return middle
+      } else if (middleOffset < offset) {
+        low = middle + 1
+      } else if (middleOffset > offset) {
+        high = middle - 1
+      }
+    }
+
+    return low > 0 ? --low : 0
+  }
+
+  // return a scroll offset from given index, can efficiency be improved more here?
+  // although the call frequency is very high, its only a superposition of numbers
+  getIndexOffset(givenIndex) {
+    if (!givenIndex) {
+      return 0
+    }
+
+    let offset = 0
+    let indexSize = 0
+    for (let index = 0; index < givenIndex; index++) {
+      // this.__getIndexOffsetCalls++
+      indexSize = this.sizes.get(this.param.uniqueIds[index])
+      offset = offset + (typeof indexSize === 'number' ? indexSize : this.getEstimateSize())
+    }
+
+    // remember last calculate index
+    this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1)
+    this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex())
+
+    return offset
+  }
+
+  // is fixed size type
+  isFixedType() {
+    return this.calcType === CALC_TYPE.FIXED
+  }
+
+  // return the real last index
+  getLastIndex() {
+    return this.param.uniqueIds.length - 1
+  }
+
+  // in some conditions range is broke, we need correct it
+  // and then decide whether need update to next range
+  checkRange(start, end) {
+    const keeps = this.param.keeps
+    const total = this.param.uniqueIds.length
+
+    // datas less than keeps, render all
+    if (total <= keeps) {
+      start = 0
+      end = this.getLastIndex()
+    } else if (end - start < keeps - 1) {
+      // if range length is less than keeps, current it base on end
+      start = end - keeps + 1
+    }
+
+    if (this.range.start !== start) {
+      this.updateRange(start, end)
+    }
+  }
+
+  // setting to a new range and re-render
+  updateRange(start, end) {
+    this.range.start = start
+    this.range.end = end
+    this.range.padFront = this.getPadFront()
+    this.range.padBehind = this.getPadBehind()
+    this.callUpdate(this.getRange())
+  }
+
+  // return end base on start
+  getEndByStart(start) {
+    const theoryEnd = start + this.param.keeps - 1
+    const trulyEnd = Math.min(theoryEnd, this.getLastIndex())
+    return trulyEnd
+  }
+
+  // return total front offset
+  getPadFront() {
+    if (this.isFixedType()) {
+      return this.fixedSizeValue * this.range.start
+    } else {
+      return this.getIndexOffset(this.range.start)
+    }
+  }
+
+  // return total behind offset
+  getPadBehind() {
+    const end = this.range.end
+    const lastIndex = this.getLastIndex()
+
+    if (this.isFixedType()) {
+      return (lastIndex - end) * this.fixedSizeValue
+    }
+
+    // if it's all calculated, return the exactly offset
+    if (this.lastCalcIndex === lastIndex) {
+      return this.getIndexOffset(lastIndex) - this.getIndexOffset(end)
+    } else {
+      // if not, use a estimated value
+      return (lastIndex - end) * this.getEstimateSize()
+    }
+  }
+
+  // get the item estimate size
+  getEstimateSize() {
+    return this.isFixedType() ? this.fixedSizeValue : (this.firstRangeAverageSize || this.param.estimateSize)
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/package.json b/tools/winscope/package.json
index 617f577..6a46337 100644
--- a/tools/winscope/package.json
+++ b/tools/winscope/package.json
@@ -6,27 +6,66 @@
   "private": true,
   "scripts": {
     "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
-    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
+    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
+    "test": "webpack --config webpack.spec.config.js && jasmine dist/bundleSpec.js"
   },
   "dependencies": {
+    "cross-env": "^7.0.2",
+    "jszip": "^3.5.0",
+    "kotlin": "^1.3.72",
+    "lodash.clonedeep": "^4.5.0",
+    "ts-loader": "^8.0.3",
+    "typescript": "^4.0.2",
     "vue": "^2.3.3",
-    "vue-material": "0.8.1"
+    "vue-context": "^5.2.0",
+    "vue-material": "^1.0.0-beta-11",
+    "vuex": "^3.4.0"
   },
   "devDependencies": {
-    "babel-core": "^6.0.0",
-    "babel-loader": "^6.0.0",
-    "babel-preset-env": "^1.5.1",
-    "cross-env": "^3.0.0",
-    "css-loader": "^0.25.0",
-    "file-loader": "^0.9.0",
-    "html-webpack-inline-source-plugin": "^0.0.9",
-    "html-webpack-plugin": "^2.30.1",
-    "loader-utils": "^1.1.0",
-    "protobufjs": "^6.8.0",
-    "style-loader": "^0.19.0",
-    "vue-loader": "^12.1.0",
-    "vue-template-compiler": "^2.3.3",
-    "webpack": "^2.6.1",
-    "webpack-dev-server": "^2.4.5"
+    "@babel/core": "^7.10.5",
+    "@babel/polyfill": "^7.10.4",
+    "@babel/preset-env": "^7.10.4",
+    "@babel/register": "^7.10.5",
+    "@jetbrains/kotlin-webpack-plugin": "^3.0.2",
+    "@testing-library/vue": "^5.1.0",
+    "@types/lodash": "^4.14.158",
+    "babel-loader": "^8.1.0",
+    "compression-webpack-plugin": "^4.0.0",
+    "cross-env": "^7.0.2",
+    "css-loader": "^3.6.0",
+    "eslint": "^7.1.0",
+    "eslint-config-google": "^0.14.0",
+    "eslint-plugin-vue": "^6.2.2",
+    "file-loader": "^6.0.0",
+    "friendly-errors-webpack-plugin": "^1.7.0",
+    "html-webpack-inline-source-plugin": "^0.0.10",
+    "html-webpack-plugin": "3.2.0",
+    "husky": "^4.2.5",
+    "jasmine": "^3.5.0",
+    "lint-staged": ">=10",
+    "loader-utils": "^2.0.0",
+    "mini-css-extract-plugin": "^0.9.0",
+    "optimize-css-assets-webpack-plugin": "^5.0.3",
+    "protobufjs": "^6.10.0",
+    "source-map-loader": "^1.0.1",
+    "style-loader": "^1.2.1",
+    "ts-loader": "^8.0.1",
+    "typescript": "^3.9.7",
+    "uglifyjs-webpack-plugin": "^2.2.0",
+    "vue-loader": "^15.9.3",
+    "vue-style-loader": "^4.1.2",
+    "vue-template-compiler": "^2.6.11",
+    "webpack": "^4.43.0",
+    "webpack-cli": "^3.3.12",
+    "webpack-dev-server": "^3.11.0",
+    "webpack-merge": "^5.0.9"
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  },
+  "lint-staged": {
+    "*.{js,vue}": "eslint --cache --fix"
   }
 }
diff --git a/tools/winscope/spec/DiffSpec.js b/tools/winscope/spec/DiffSpec.js
new file mode 100644
index 0000000..68dfa4e
--- /dev/null
+++ b/tools/winscope/spec/DiffSpec.js
@@ -0,0 +1,267 @@
+import { DiffGenerator, DiffType } from "../src/utils/diff.js";
+import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
+
+function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
+    const diffTree = new DiffGenerator(newTree)
+        .compareWith(oldTree)
+        .withUniqueNodeId(node => node.id)
+        .generateDiffTree();
+
+    expect(diffTree).toEqual(expectedDiffTree);
+}
+
+describe("DiffGenerator", () => {
+    it("can generate a simple add diff", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, [
+                new Node({ id: 5 }, []),
+            ]),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const diffTree = new DiffGenerator(newTree)
+            .compareWith(oldTree)
+            .withUniqueNodeId(node => node.id)
+            .withModifiedCheck(() => false)
+            .generateDiffTree();
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+                new DiffNode({ id: 3 }, DiffType.NONE, [
+                    new DiffNode({ id: 5 }, DiffType.ADDED, []),
+                ]),
+                new DiffNode({ id: 4 }, DiffType.NONE, []),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can generate a simple delete diff", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, [
+                new Node({ id: 5 }, []),
+            ]),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+                new DiffNode({ id: 3 }, DiffType.NONE, [
+                    new DiffNode({ id: 5 }, DiffType.DELETED, []),
+                ]),
+                new DiffNode({ id: 4 }, DiffType.NONE, []),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can generate a simple move diff", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, [
+                new Node({ id: 5 }, []),
+            ]),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+            new Node({ id: 4 }, [
+                new Node({ id: 5 }, []),
+            ]),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+                new DiffNode({ id: 3 }, DiffType.NONE, [
+                    new DiffNode({ id: 5 }, DiffType.DELETED_MOVE, []),
+                ]),
+                new DiffNode({ id: 4 }, DiffType.NONE, [
+                    new DiffNode({ id: 5 }, DiffType.ADDED_MOVE, []),
+                ]),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can generate a simple modified diff", () => {
+        const oldTree = new Node({ id: 1, data: "xyz" }, [
+            new Node({ id: 2, data: "abc" }, []),
+            new Node({ id: 3, data: "123" }, []),
+        ]);
+
+        const newTree = new Node({ id: 1, data: "xyz" }, [
+            new Node({ id: 2, data: "def" }, []),
+            new Node({ id: 3, data: "123" }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1, data: "xyz" }, DiffType.NONE, [
+                new DiffNode({ id: 2, data: "def" }, DiffType.MODIFIED, []),
+                new DiffNode({ id: 3, data: "123" }, DiffType.NONE, []),
+            ])
+        );
+
+        const diffTree = new DiffGenerator(newTree)
+            .compareWith(oldTree)
+            .withUniqueNodeId(node => node.id)
+            .withModifiedCheck(
+                (newNode, oldNode) => newNode.data != oldNode.data)
+            .generateDiffTree();
+
+        expect(diffTree).toEqual(expectedDiffTree);
+    });
+
+    it("can handle move and inner addition diff", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, [
+                new Node({ id: 4 }, []),
+            ]),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, [
+                new Node({ id: 4 }, [
+                    new Node({ id: 5 }, []),
+                ]),
+            ]),
+            new Node({ id: 3 }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, [
+                    new DiffNode({ id: 4 }, DiffType.ADDED_MOVE, [
+                        new DiffNode({ id: 5 }, DiffType.ADDED, []),
+                    ]),
+                ]),
+                new DiffNode({ id: 3 }, DiffType.NONE, [
+                    new DiffNode({ id: 4 }, DiffType.DELETED_MOVE, []),
+                ]),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can handle move within same level", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 3 }, []),
+            new Node({ id: 2 }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 3 }, DiffType.NONE, []),
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can handle addition within middle of level", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 4 }, []),
+            new Node({ id: 3 }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+                new DiffNode({ id: 4 }, DiffType.ADDED, []),
+                new DiffNode({ id: 3 }, DiffType.NONE, []),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("can handle deletion within middle of level", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, []),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 4 }, []),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, []),
+                new DiffNode({ id: 3 }, DiffType.DELETED, []),
+                new DiffNode({ id: 4 }, DiffType.NONE, []),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+
+    it("fully visits deletes nodes", () => {
+        const oldTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, [
+                new Node({ id: 3 }, [
+                    new Node({ id: 4 }, []),
+                ]),
+            ]),
+        ]);
+
+        const newTree = new Node({ id: 1 }, [
+            new Node({ id: 2 }, []),
+            new Node({ id: 3 }, [
+                new Node({ id: 4 }, []),
+            ]),
+        ]);
+
+        const expectedDiffTree = toPlainObject(
+            new DiffNode({ id: 1 }, DiffType.NONE, [
+                new DiffNode({ id: 2 }, DiffType.NONE, [
+                    new DiffNode({ id: 3 }, DiffType.DELETED_MOVE, [
+                        new DiffNode({ id: 4 }, DiffType.DELETED_MOVE, []),
+                    ]),
+                ]),
+                new DiffNode({ id: 3 }, DiffType.ADDED_MOVE, [
+                    new DiffNode({ id: 4 }, DiffType.NONE, []),
+                ]),
+            ])
+        );
+
+        checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree);
+    });
+});
\ No newline at end of file
diff --git a/tools/winscope/spec/ObjectTransformerSpec.js b/tools/winscope/spec/ObjectTransformerSpec.js
new file mode 100644
index 0000000..4d84fd9
--- /dev/null
+++ b/tools/winscope/spec/ObjectTransformerSpec.js
@@ -0,0 +1,184 @@
+import { DiffType } from "../src/utils/diff.js";
+import { ObjectTransformer } from "../src/transform.js";
+import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
+
+class ObjNode extends Node {
+    constructor(name, children, combined) {
+        const nodeDef = {
+            kind: '',
+            name: name,
+        };
+        if (combined) {
+            nodeDef.combined = true;
+        }
+        super(nodeDef, children);
+    }
+}
+
+class ObjDiffNode extends DiffNode {
+    constructor(name, diffType, children, combined) {
+        const nodeDef = {
+            kind: '',
+            name: name,
+        };
+        if (combined) {
+            nodeDef.combined = true;
+        }
+        super(nodeDef, diffType, children);
+    }
+}
+
+describe("ObjectTransformer", () => {
+    it("can transform a simple object", () => {
+        const obj = {
+            obj: {
+                string: 'string',
+                number: 3,
+            },
+            array: [
+                {
+                    nested: "item",
+                },
+                "two",
+            ],
+        };
+
+        const expectedTransformedObj = toPlainObject(
+            new ObjNode('root', [
+                new ObjNode('obj', [
+                    new ObjNode('string: string', [], true),
+                    new ObjNode('number: 3', [], true),
+                ]),
+                new ObjNode('array', [
+                    new ObjNode('0', [
+                        new ObjNode('nested: item', [], true),
+                    ]),
+                    new ObjNode("1: two", [], true),
+                ]),
+            ])
+        );
+
+        const transformedObj = new ObjectTransformer(obj, 'root')
+            .setOptions({ formatter: () => { } })
+            .transform();
+
+        expect(transformedObj).toEqual(expectedTransformedObj);
+    });
+
+    it("handles null as expected", () => {
+        const obj = {
+            obj: {
+                null: null,
+            },
+        }
+
+        const expectedTransformedObj = toPlainObject(
+            new ObjNode('root', [
+                new ObjNode('obj', [
+                    new ObjNode('null: null', [], true),
+                ]),
+            ])
+        );
+
+        const transformedObj = new ObjectTransformer(obj, 'root')
+            .setOptions({ formatter: () => { } })
+            .transform();
+
+        expect(transformedObj).toEqual(expectedTransformedObj);
+    });
+
+    it("can generate a simple add diff", () => {
+        const oldObj = {
+            a: {
+                b: 1,
+            },
+            c: 2,
+        };
+
+        const newObj = {
+            a: {
+                b: 1,
+                d: 3,
+            },
+            c: 2,
+        };
+
+        const expectedTransformedObj = toPlainObject(
+            new ObjDiffNode('root', DiffType.NONE, [
+                new ObjDiffNode('a', DiffType.NONE, [
+                    new ObjDiffNode('b: 1', DiffType.NONE, [], true),
+                    new ObjDiffNode('d: 3', DiffType.ADDED, [], true),
+                ]),
+                new ObjDiffNode('c: 2', DiffType.NONE, [], true),
+            ])
+        );
+
+        const transformedObj = new ObjectTransformer(newObj, 'root')
+            .setOptions({ formatter: () => { } })
+            .withDiff(oldObj)
+            .transform();
+
+        expect(transformedObj).toEqual(expectedTransformedObj);
+    });
+
+    it("can handle null", () => {
+        const oldObj = {
+            a: null,
+        };
+
+        const newObj = {
+            a: 1,
+        };
+
+        const expectedTransformedObj = toPlainObject(
+            new ObjDiffNode('root', DiffType.NONE, [
+                new ObjDiffNode('a', DiffType.NONE, [
+                    new ObjDiffNode('1', DiffType.ADDED, []),
+                    new ObjDiffNode('null', DiffType.DELETED, []),
+                ]),
+            ])
+        );
+
+        const transformedObj = new ObjectTransformer(newObj, 'root')
+            .setOptions({ formatter: () => { } })
+            .withDiff(oldObj)
+            .transform();
+
+        expect(transformedObj).toEqual(expectedTransformedObj);
+    });
+
+    it("can handle nested null", () => {
+        const oldObj = {
+            a: {
+                b: null,
+            },
+            c: 2,
+        };
+
+        const newObj = {
+            a: {
+                b: 1,
+            },
+            c: 2,
+        };
+
+        const expectedTransformedObj = toPlainObject(
+            new ObjDiffNode('root', DiffType.NONE, [
+                new ObjDiffNode('a', DiffType.NONE, [
+                    new ObjDiffNode('b', DiffType.NONE, [
+                        new ObjDiffNode('1', DiffType.ADDED, []),
+                        new ObjDiffNode('null', DiffType.DELETED, []),
+                    ]),
+                ]),
+                new ObjDiffNode('c: 2', DiffType.NONE, [], true),
+            ])
+        );
+
+        const transformedObj = new ObjectTransformer(newObj, 'root')
+            .setOptions({ formatter: () => { } })
+            .withDiff(oldObj)
+            .transform();
+
+        expect(transformedObj).toEqual(expectedTransformedObj);
+    });
+});
\ No newline at end of file
diff --git a/tools/winscope/spec/ProtoTransformSpec.js b/tools/winscope/spec/ProtoTransformSpec.js
new file mode 100644
index 0000000..8db736b
--- /dev/null
+++ b/tools/winscope/spec/ProtoTransformSpec.js
@@ -0,0 +1,25 @@
+import { detectAndDecode, decodeAndTransformProto, FILE_TYPES } from '../src/decode';
+import fs from 'fs';
+import path from 'path';
+
+const layers_traces = [
+  require('./traces/layers_trace/layers_trace_emptyregion.pb'),
+  require('./traces/layers_trace/layers_trace_invalid_layer_visibility.pb'),
+  require('./traces/layers_trace/layers_trace_orphanlayers.pb'),
+  require('./traces/layers_trace/layers_trace_root.pb'),
+  require('./traces/layers_trace/layers_trace_root_aosp.pb'),
+];
+
+describe("Proto Transformations", () => {
+  it("can transform surface flinger traces", () => {
+    for (const trace of layers_traces) {
+      fs.readFileSync(path.resolve(__dirname, trace));
+      const traceBuffer = fs.readFileSync(path.resolve(__dirname, trace));
+
+      const buffer = new Uint8Array(traceBuffer);
+      const data = decodeAndTransformProto(buffer, FILE_TYPES.layers_trace, true);
+
+      expect(true).toBe(true);
+    }
+  });
+});
\ No newline at end of file
diff --git a/tools/winscope/spec/SimplifiedLayerNamesSpec.js b/tools/winscope/spec/SimplifiedLayerNamesSpec.js
new file mode 100644
index 0000000..ce2f83f
--- /dev/null
+++ b/tools/winscope/spec/SimplifiedLayerNamesSpec.js
@@ -0,0 +1,24 @@
+import { getSimplifiedLayerName } from "../src/utils/names";
+
+const simplifications = {
+  "WindowToken{38eae45 android.os.BinderProxy@398bebc}#0": "WindowToken",
+  "7d8c460 NavigationBar0#0": "NavigationBar0#0",
+  "Surface(name=d2965b1 NavigationBar0)/@0xe4380b2 - animation-leash#2": "Surface - animation-leash#2",
+  "com.breel.wallpapers19.doodle.wallpaper.variations.DoodleWallpaperV1#0": "DoodleWallpaperV1#0",
+  "ActivityRecord{825ebe6 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity#0": "ActivityRecord",
+  "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0": "NexusLauncherActivity#0",
+  "com.android.settings/com.android.settings.Settings$UsbDetailsActivity#0": "Settings$UsbDetailsActivity#0",
+  "7d8c460 com.google.android.calendar/com.google.android.calendar.AllInOneCalendarActivity#0": "AllInOneCalendarActivity#0",
+  "WallpaperWindowToken{ad25afe token=android.os.Binder@8ab6b9}#0": "WallpaperWindowToken",
+};
+
+describe("getSimplifiedLayerName", () => {
+  it("simplifies traces as expected", () => {
+    for (const longName in simplifications) {
+      const expectedSimplifiedName = simplifications[longName];
+      const actualSimplifiedName = getSimplifiedLayerName(longName);
+
+      expect(actualSimplifiedName).toBe(expectedSimplifiedName);
+    }
+  });
+});
\ No newline at end of file
diff --git a/tools/winscope/spec/support/jasmine.json b/tools/winscope/spec/support/jasmine.json
new file mode 100644
index 0000000..32579a5
--- /dev/null
+++ b/tools/winscope/spec/support/jasmine.json
@@ -0,0 +1,11 @@
+{
+  "spec_dir": "spec",
+  "spec_files": [
+    "**/*[sS]pec.js"
+  ],
+  "helpers": [
+    "../node_modules/@babel/register/lib/node.js"
+  ],
+  "stopSpecOnExpectationFailure": false,
+  "random": false
+}
\ No newline at end of file
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb
new file mode 100644
index 0000000..98ee6f3
--- /dev/null
+++ b/tools/winscope/spec/traces/layers_trace/layers_trace_emptyregion.pb
Binary files differ
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb
new file mode 100644
index 0000000..20572d7
--- /dev/null
+++ b/tools/winscope/spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb
Binary files differ
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_orphanlayers.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_orphanlayers.pb
new file mode 100644
index 0000000..af40797
--- /dev/null
+++ b/tools/winscope/spec/traces/layers_trace/layers_trace_orphanlayers.pb
Binary files differ
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_root.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_root.pb
new file mode 100644
index 0000000..d961714
--- /dev/null
+++ b/tools/winscope/spec/traces/layers_trace/layers_trace_root.pb
Binary files differ
diff --git a/tools/winscope/spec/traces/layers_trace/layers_trace_root_aosp.pb b/tools/winscope/spec/traces/layers_trace/layers_trace_root_aosp.pb
new file mode 100644
index 0000000..666b328
--- /dev/null
+++ b/tools/winscope/spec/traces/layers_trace/layers_trace_root_aosp.pb
Binary files differ
diff --git a/tools/winscope/spec/traces/wl_trace.pb b/tools/winscope/spec/traces/wl_trace.pb
new file mode 100644
index 0000000..7e1f007
--- /dev/null
+++ b/tools/winscope/spec/traces/wl_trace.pb
Binary files differ
diff --git a/tools/winscope/spec/utils/tree.js b/tools/winscope/spec/utils/tree.js
new file mode 100644
index 0000000..379ebaf
--- /dev/null
+++ b/tools/winscope/spec/utils/tree.js
@@ -0,0 +1,33 @@
+class Node {
+    constructor(nodeDef, children) {
+        Object.assign(this, nodeDef);
+        this.children = children;
+    }
+}
+
+class DiffNode extends Node {
+    constructor(nodeDef, diffType, children) {
+        super(nodeDef, children);
+        this.diff = { type: diffType };
+    }
+}
+
+function isPrimitive(test) {
+    return test !== Object(test);
+};
+
+function toPlainObject(theClass) {
+    if (isPrimitive(theClass)) {
+        return theClass;
+    } else if (Array.isArray(theClass)) {
+        return theClass.map(item => toPlainObject(item));
+    } else {
+        const keys = Object.getOwnPropertyNames(Object.assign({}, theClass));
+        return keys.reduce((classAsObj, key) => {
+            classAsObj[key] = toPlainObject(theClass[key]);
+            return classAsObj;
+        }, {});
+    }
+}
+
+export { Node, DiffNode, toPlainObject };
\ No newline at end of file
diff --git a/tools/winscope/src/App.vue b/tools/winscope/src/App.vue
index 6e72431..1b0d5ef 100644
--- a/tools/winscope/src/App.vue
+++ b/tools/winscope/src/App.vue
@@ -14,124 +14,119 @@
 -->
 <template>
   <div id="app">
-    <md-whiteframe md-tag="md-toolbar">
-      <h1 class="md-title" style="flex: 1">{{title}}</h1>
-      <a class="md-button md-accent md-raised md-theme-default" @click="clear()" v-if="dataLoaded">Clear</a>
-    </md-whiteframe>
-    <div class="main-content">
-      <md-layout v-if="!dataLoaded" class="m-2">
-        <dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
-        <datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
-      </md-layout>
-      <md-card v-if="dataLoaded">
-        <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
-          <h2 class="md-title">Timeline</h2>
-          <datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" />
-        </md-whiteframe>
-        <md-list>
-          <md-list-item v-for="(file, idx) in files" :key="file.filename">
-            <md-icon>{{file.type.icon}}</md-icon>
-            <timeline :items="file.timeline" :selected-index="file.selectedIndex" :scale="scale" @item-selected="onTimelineItemSelected($event, idx)" class="timeline" />
-          </md-list-item>
-        </md-list>
-      </md-card>
-      <dataview v-for="file in files" :key="file.filename" :ref="file.filename" :store="store" :file="file" @focus="onDataViewFocus(file.filename)" />
-    </div>
+    <md-app>
+      <md-app-toolbar md-tag="md-toolbar" class="top-toolbar">
+        <h1 class="md-title" style="flex: 1">{{title}}</h1>
+        <md-button
+          class="md-primary md-theme-default download-all-btn"
+          @click="downloadAsZip(files)"
+          v-if="dataLoaded"
+        >Download All</md-button>
+        <md-button
+          class="md-accent md-raised md-theme-default clear-btn"
+          style="box-shadow: none;"
+          @click="clear()"
+          v-if="dataLoaded"
+        >Clear</md-button>
+      </md-app-toolbar>
+
+      <md-app-content class="main-content" :style="mainContentStyle">
+        <section class="data-inputs" v-if="!dataLoaded">
+          <div class="input">
+            <dataadb class="adbinput" ref="adb" :store="store"
+              @dataReady="onDataReady" @statusChange="setStatus" />
+          </div>
+          <div class="input">
+            <datainput class="fileinput" ref="input" :store="store"
+              @dataReady="onDataReady" @statusChange="setStatus" />
+          </div>
+        </section>
+
+        <section class="data-view">
+          <div
+            class="data-view-container"
+            v-for="file in dataViewFiles"
+            :key="file.type"
+          >
+            <dataview
+              :ref="file.type"
+              :store="store"
+              :file="file"
+              @click="onDataViewFocus(file)"
+            />
+          </div>
+
+          <overlay
+            :store="store"
+            :ref="overlayRef"
+            v-if="dataLoaded"
+            v-on:bottom-nav-height-change="handleBottomNavHeightChange"
+          />
+        </section>
+      </md-app-content>
+    </md-app>
   </div>
 </template>
 <script>
-import TreeView from './TreeView.vue'
-import Timeline from './Timeline.vue'
-import Rects from './Rects.vue'
-import DataView from './DataView.vue'
-import DataInput from './DataInput.vue'
-import LocalStore from './localstore.js'
-import DataAdb from './DataAdb.vue'
-import DataFilter from './DataFilter.vue'
+import Overlay from './Overlay.vue';
+import DataView from './DataView.vue';
+import DataInput from './DataInput.vue';
+import LocalStore from './localstore.js';
+import DataAdb from './DataAdb.vue';
+import FileType from './mixins/FileType.js';
+import SaveAsZip from './mixins/SaveAsZip';
+import FocusedDataViewFinder from './mixins/FocusedDataViewFinder';
+import {DIRECTION} from './utils/utils';
+import {NAVIGATION_STYLE} from './utils/consts';
 
-const APP_NAME = "Winscope"
+const APP_NAME = 'Winscope';
 
-// Find the index of the last element matching the predicate in a sorted array
-function findLastMatchingSorted(array, predicate) {
-  var a = 0;
-  var b = array.length - 1;
-  while (b - a > 1) {
-    var m = Math.floor((a + b) / 2);
-    if (predicate(array, m)) {
-      a = m;
-    } else {
-      b = m - 1;
-    }
-  }
-  return predicate(array, b) ? b : a;
-}
+const CONTENT_BOTTOM_PADDING = 25;
 
 export default {
   name: 'app',
+  mixins: [FileType, SaveAsZip, FocusedDataViewFinder],
   data() {
     return {
-      files: [],
       title: APP_NAME,
-      currentTimestamp: 0,
       activeDataView: null,
+      // eslint-disable-next-line new-cap
       store: LocalStore('app', {
         flattened: false,
         onlyVisible: false,
+        simplifyNames: true,
         displayDefaults: true,
+        navigationStyle: NAVIGATION_STYLE.GLOBAL,
       }),
-    }
+      overlayRef: 'overlay',
+      mainContentStyle: {
+        'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`,
+      },
+    };
   },
   created() {
     window.addEventListener('keydown', this.onKeyDown);
+    window.addEventListener('scroll', this.onScroll);
     document.title = this.title;
   },
+  destroyed() {
+    window.removeEventListener('keydown', this.onKeyDown);
+    window.removeEventListener('scroll', this.onScroll);
+  },
   methods: {
     clear() {
-      this.files.forEach(function(item) { item.destroy(); })
-      this.files = [];
-      this.activeDataView = null;
+      this.$store.commit('clearFiles');
     },
-    onTimelineItemSelected(index, timelineIndex) {
-      this.files[timelineIndex].selectedIndex = index;
-      var t = parseInt(this.files[timelineIndex].timeline[index]);
-      for (var i = 0; i < this.files.length; i++) {
-        if (i != timelineIndex) {
-          this.files[i].selectedIndex = findLastMatchingSorted(this.files[i].timeline, function(array, idx) {
-            return parseInt(array[idx]) <= t;
-          });
-        }
-      }
-      this.currentTimestamp = t;
-    },
-    advanceTimeline(direction) {
-      var closestTimeline = -1;
-      var timeDiff = Infinity;
-      for (var idx = 0; idx < this.files.length; idx++) {
-        var file = this.files[idx];
-        var cur = file.selectedIndex;
-        if (cur + direction < 0 || cur + direction >= this.files[idx].timeline.length) {
-          continue;
-        }
-        var d = Math.abs(parseInt(file.timeline[cur + direction]) - this.currentTimestamp);
-        if (timeDiff > d) {
-          timeDiff = d;
-          closestTimeline = idx;
-        }
-      }
-      if (closestTimeline >= 0) {
-        this.files[closestTimeline].selectedIndex += direction;
-        this.currentTimestamp = parseInt(this.files[closestTimeline].timeline[this.files[closestTimeline].selectedIndex]);
-      }
-    },
-    onDataViewFocus(view) {
-      this.activeDataView = view;
+    onDataViewFocus(file) {
+      this.$store.commit('setActiveFile', file);
+      this.activeDataView = file.type;
     },
     onKeyDown(event) {
       event = event || window.event;
       if (event.keyCode == 37 /* left */ ) {
-        this.advanceTimeline(-1);
+        this.$store.dispatch('advanceTimeline', DIRECTION.BACKWARD);
       } else if (event.keyCode == 39 /* right */ ) {
-        this.advanceTimeline(1);
+        this.$store.dispatch('advanceTimeline', DIRECTION.FORWARD);
       } else if (event.keyCode == 38 /* up */ ) {
         this.$refs[this.activeView][0].arrowUp();
       } else if (event.keyCode == 40 /* down */ ) {
@@ -143,7 +138,8 @@
       return true;
     },
     onDataReady(files) {
-      this.files = files;
+      this.$store.dispatch('setFiles', files);
+      this.updateFocusedView();
     },
     setStatus(status) {
       if (status) {
@@ -151,41 +147,80 @@
       } else {
         this.title = APP_NAME;
       }
-    }
+    },
+    handleBottomNavHeightChange(newHeight) {
+      this.$set(
+          this.mainContentStyle,
+          'padding-bottom',
+          `${ CONTENT_BOTTOM_PADDING + newHeight }px`,
+      );
+    },
   },
   computed: {
-    prettyDump: function() { return JSON.stringify(this.dump, null, 2); },
-    dataLoaded: function() { return this.files.length > 0 },
-    scale() {
-      var mx = Math.max(...(this.files.map(f => Math.max(...f.timeline))));
-      var mi = Math.min(...(this.files.map(f => Math.min(...f.timeline))));
-      return [mi, mx];
+    files() {
+      return this.$store.getters.sortedFiles;
     },
-    activeView: function() {
+    prettyDump() {
+      return JSON.stringify(this.dump, null, 2);
+    },
+    dataLoaded() {
+      return this.files.length > 0;
+    },
+    activeView() {
       if (!this.activeDataView && this.files.length > 0) {
-        this.activeDataView = this.files[0].filename;
+        // eslint-disable-next-line vue/no-side-effects-in-computed-properties
+        this.activeDataView = this.files[0].type;
       }
       return this.activeDataView;
-    }
+    },
+    dataViewFiles() {
+      return this.files.filter((f) => this.hasDataView(f));
+    },
   },
   watch: {
     title() {
       document.title = this.title;
-    }
+    },
   },
   components: {
-    'timeline': Timeline,
-    'dataview': DataView,
-    'datainput': DataInput,
-    'dataadb': DataAdb,
-    'datafilter': DataFilter,
+    overlay: Overlay,
+    dataview: DataView,
+    datainput: DataInput,
+    dataadb: DataAdb,
   },
-}
-
+};
 </script>
 <style>
-.main-content>* {
-  margin: 1em;
+@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap');
+
+#app .md-app-container {
+  /* Get rid of tranforms which prevent fixed position from being used */
+  transform: none!important;
+  min-height: 100vh;
+}
+
+#app .top-toolbar {
+  box-shadow: none;
+  background-color: #fff;
+  background-color: var(--md-theme-default-background, #fff);
+  border-bottom: thin solid rgba(0,0,0,.12);
+  padding:  0 40px;
+}
+
+#app .top-toolbar .md-title {
+  font-family: 'Open Sans', sans-serif;
+  white-space: nowrap;
+  color: #5f6368;
+  margin: 0;
+  padding: 0;
+  font-size: 22px;
+  letter-spacing: 0;
+  font-weight: 600;
+}
+
+.data-view {
+  display: flex;
+  flex-direction: column;
 }
 
 .card-toolbar {
@@ -201,10 +236,6 @@
   flex-wrap: wrap;
 }
 
-.md-layout > .md-card {
-  margin: 0.5em;
-}
-
 .md-button {
   margin-top: 1em
 }
@@ -219,13 +250,33 @@
   padding: 0;
 }
 
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-
 a {
   color: #42b983;
 }
 
+.data-inputs {
+  display: flex;
+  flex-wrap: wrap;
+  height: 100%;
+  width: 100%;
+  align-self: center;
+  /* align-items: center; */
+  align-content: center;
+  justify-content: center;
+}
+
+.data-inputs .input {
+  padding: 15px;
+  flex: 1 1 0;
+  max-width: 840px;
+  /* align-self: center; */
+}
+
+.data-inputs .input > div {
+  height: 100%;
+}
+
+.data-view-container {
+  padding: 25px 20px 0 20px;
+}
 </style>
diff --git a/tools/winscope/src/DataAdb.vue b/tools/winscope/src/DataAdb.vue
index 39665dd..2d16f8e 100644
--- a/tools/winscope/src/DataAdb.vue
+++ b/tools/winscope/src/DataAdb.vue
@@ -13,12 +13,12 @@
      limitations under the License.
 -->
 <template>
-  <md-card style="min-width: 50em">
+  <flat-card style="min-width: 50em">
     <md-card-header>
       <div class="md-title">ADB Connect</div>
     </md-card-header>
     <md-card-content v-if="status === STATES.CONNECTING">
-      <md-spinner md-indeterminate></md-spinner>
+      <md-progress-spinner md-indeterminate></md-progress-spinner>
     </md-card-content>
     <md-card-content v-if="status === STATES.NO_PROXY">
       <md-icon class="md-accent">error</md-icon>
@@ -30,9 +30,9 @@
         <pre>python3 $ANDROID_BUILD_TOP/development/tools/winscope/adb_proxy/winscope_proxy.py</pre>
         <p>Or get it from the AOSP repository.</p>
       </div>
-      <div class="md-layout md-gutter">
-        <md-button class="md-accent md-raised" :href="downloadProxyUrl">Download from AOSP</md-button>
-        <md-button class="md-raised md-accent" @click="restart">Retry</md-button>
+      <div class="md-layout">
+        <md-button class="md-accent" :href="downloadProxyUrl">Download from AOSP</md-button>
+        <md-button class="md-accent" @click="restart">Retry</md-button>
       </div>
     </md-card-content>
     <md-card-content v-if="status === STATES.INVALID_VERSION">
@@ -44,50 +44,64 @@
         <pre>python3 $ANDROID_BUILD_TOP/development/tools/winscope/adb_proxy/winscope_proxy.py</pre>
         <p>Or get it from the AOSP repository.</p>
       </div>
-      <div class="md-layout md-gutter">
-        <md-button class="md-accent md-raised" :href="downloadProxyUrl">Download from AOSP</md-button>
-        <md-button class="md-raised md-accent" @click="restart">Retry</md-button>
+      <div class="md-layout">
+        <md-button class="md-accent" :href="downloadProxyUrl">Download from AOSP</md-button>
+        <md-button class="md-accent" @click="restart">Retry</md-button>
       </div>
     </md-card-content>
     <md-card-content v-if="status === STATES.UNAUTH">
       <md-icon class="md-accent">lock</md-icon>
       <span class="md-subheading">Proxy authorisation required</span>
-      <md-input-container>
+      <md-field>
         <label>Enter Winscope proxy token</label>
         <md-input v-model="adbStore.proxyKey"></md-input>
-      </md-input-container>
+      </md-field>
       <div class="md-body-2">The proxy token is printed to console on proxy launch, copy and paste it above.</div>
-      <div class="md-layout md-gutter">
-        <md-button class="md-accent md-raised" @click="restart">Connect</md-button>
+      <div class="md-layout">
+        <md-button class="md-primary" @click="restart">Connect</md-button>
       </div>
     </md-card-content>
     <md-card-content v-if="status === STATES.DEVICES">
       <div class="md-subheading">{{ Object.keys(devices).length > 0 ? "Connected devices:" : "No devices detected" }}</div>
       <md-list>
         <md-list-item v-for="(device, id) in devices" :key="id" @click="selectDevice(id)" :disabled="!device.authorised">
-          <md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon><span>{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
+          <md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon>
+          <span class="md-list-item-text">{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
         </md-list-item>
       </md-list>
-      <md-spinner :md-size="30" md-indeterminate></md-spinner>
+      <md-progress-spinner :md-size="30" md-indeterminate></md-progress-spinner>
     </md-card-content>
     <md-card-content v-if="status === STATES.START_TRACE">
-      <md-list>
-        <md-list-item>
-          <md-icon>smartphone</md-icon><span>{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
-        </md-list-item>
-      </md-list>
-      <div>
-        <p>Trace targets:</p>
-        <md-checkbox v-for="file in TRACE_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox>
+      <div class="device-choice">
+        <md-list>
+          <md-list-item>
+            <md-icon>smartphone</md-icon>
+            <span class="md-list-item-text">{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
+          </md-list-item>
+        </md-list>
+        <md-button class="md-primary" @click="resetLastDevice">Change device</md-button>
       </div>
-      <div>
-        <p>Dump targets:</p>
-        <md-checkbox v-for="file in DUMP_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox>
+      <div class="trace-section">
+        <h3>Trace targets:</h3>
+        <div class="selection">
+          <md-checkbox class="md-primary" v-for="traceKey in Object.keys(TRACES)" :key="traceKey" v-model="adbStore[traceKey]">{{TRACES[traceKey].name}}</md-checkbox>
+        </div>
+        <div class="trace-config" v-for="traceKey in Object.keys(TRACE_CONFIG)" :key="traceKey">
+            <h4>{{TRACES[traceKey].name}} config</h4>
+            <div class="selection">
+              <md-checkbox class="md-primary" v-for="config in TRACE_CONFIG[traceKey]" :key="config" v-model="adbStore[config]">{{config}}</md-checkbox>
+            </div>
+        </div>
+        <md-button class="md-primary trace-btn" @click="startTrace">Start trace</md-button>
       </div>
-      <div class="md-layout md-gutter">
-        <md-button class="md-accent md-raised" @click="startTrace">Start trace</md-button>
-        <md-button class="md-accent md-raised" @click="dumpState">Dump state</md-button>
-        <md-button class="md-raised" @click="resetLastDevice">Device list</md-button>
+      <div class="dump-section">
+        <h3>Dump targets:</h3>
+        <div class="selection">
+          <md-checkbox class="md-primary" v-for="dumpKey in Object.keys(DUMPS)" :key="dumpKey" v-model="adbStore[dumpKey]">{{DUMPS[dumpKey].name}}</md-checkbox>
+        </div>
+        <div class="md-layout">
+          <md-button class="md-primary dump-btn" @click="dumpState">Dump state</md-button>
+        </div>
       </div>
     </md-card-content>
     <md-card-content v-if="status === STATES.ERROR">
@@ -96,24 +110,25 @@
       <pre>
         {{ errorText }}
       </pre>
-      <md-button class="md-raised md-accent" @click="restart">Retry</md-button>
+      <md-button class="md-primary" @click="restart">Retry</md-button>
     </md-card-content>
     <md-card-content v-if="status === STATES.END_TRACE">
       <span class="md-subheading">Tracing...</span>
-      <md-progress md-indeterminate></md-progress>
-      <div class="md-layout md-gutter">
-        <md-button class="md-accent md-raised" @click="endTrace">End trace</md-button>
+      <md-progress-bar md-mode="indeterminate"></md-progress-bar>
+      <div class="md-layout">
+        <md-button class="md-primary" @click="endTrace">End trace</md-button>
       </div>
     </md-card-content>
     <md-card-content v-if="status === STATES.LOAD_DATA">
       <span class="md-subheading">Loading data...</span>
-      <md-progress :md-progress="loadProgress"></md-progress>
+      <md-progress-bar md-mode="determinate" :md-value="loadProgress"></md-progress-bar>
     </md-card-content>
-  </md-card>
+  </flat-card>
 </template>
 <script>
-import { FILE_TYPES, DATA_TYPES } from './decode.js'
-import LocalStore from './localstore.js'
+import {FILE_DECODERS, FILE_TYPES} from './decode.js';
+import LocalStore from './localstore.js';
+import FlatCard from './components/FlatCard.vue';
 
 const STATES = {
   ERROR: 0,
@@ -125,40 +140,92 @@
   START_TRACE: 6,
   END_TRACE: 7,
   LOAD_DATA: 8,
-}
+};
 
-const WINSCOPE_PROXY_VERSION = "0.5"
-const WINSCOPE_PROXY_URL = "http://localhost:5544"
+const WINSCOPE_PROXY_VERSION = '0.8';
+const WINSCOPE_PROXY_URL = 'http://localhost:5544';
 const PROXY_ENDPOINTS = {
-  DEVICES: "/devices/",
-  START_TRACE: "/start/",
-  END_TRACE: "/end/",
-  DUMP: "/dump/",
-  FETCH: "/fetch/",
-  STATUS: "/status/",
-}
-const TRACE_FILES = [
-  "window_trace",
-  "layers_trace",
-  "screen_recording",
-  "transaction",
-  "proto_log"
-]
-const DUMP_FILES = [
-  "window_dump",
-  "layers_dump"
-]
-const CAPTURE_FILES = TRACE_FILES.concat(DUMP_FILES)
+  DEVICES: '/devices/',
+  START_TRACE: '/start/',
+  END_TRACE: '/end/',
+  CONFIG_TRACE: '/configtrace/',
+  DUMP: '/dump/',
+  FETCH: '/fetch/',
+  STATUS: '/status/',
+};
+
+const TRACES = {
+  'window_trace': {
+    name: 'Window Manager',
+  },
+  'layers_trace': {
+    name: 'Surface Flinger',
+  },
+  'transaction': {
+    name: 'Transactions',
+  },
+  'proto_log': {
+    name: 'ProtoLog',
+  },
+  'screen_recording': {
+    name: 'Screen Recording',
+  },
+  'ime_trace_clients': {
+    name: 'Input Method Clients',
+  },
+  'ime_trace_service': {
+    name: 'Input Method Service',
+  },
+  'ime_trace_managerservice': {
+    name: 'Input Method Manager Service',
+  },
+};
+
+const TRACE_CONFIG = {
+  'layers_trace': [
+    'composition',
+    'metadata',
+    'hwc',
+  ],
+};
+
+const DUMPS = {
+  'window_dump': {
+    name: 'Window Manager',
+  },
+  'layers_dump': {
+    name: 'Surface Flinger',
+  },
+};
+
+const proxyFileTypeAdapter = {
+  'window_trace': FILE_TYPES.WINDOW_MANAGER_TRACE,
+  'layers_trace': FILE_TYPES.SURFACE_FLINGER_TRACE,
+  'wl_trace': FILE_TYPES.WAYLAND_TRACE,
+  'layers_dump': FILE_TYPES.SURFACE_FLINGER_DUMP,
+  'window_dump': FILE_TYPES.WINDOW_MANAGER_DUMP,
+  'wl_dump': FILE_TYPES.WAYLAND_DUMP,
+  'screen_recording': FILE_TYPES.SCREEN_RECORDING,
+  'transactions': FILE_TYPES.TRANSACTIONS_TRACE,
+  'proto_log': FILE_TYPES.PROTO_LOG,
+  'system_ui_trace': FILE_TYPES.SYSTEM_UI,
+  'launcher_trace': FILE_TYPES.LAUNCHER,
+  'ime_trace_clients': FILE_TYPES.IME_TRACE_CLIENTS,
+  'ime_trace_service': FILE_TYPES.IME_TRACE_SERVICE,
+  'ime_trace_managerservice': FILE_TYPES.IME_TRACE_MANAGERSERVICE,
+};
+
+const CONFIGS = Object.keys(TRACE_CONFIG).flatMap((file) => TRACE_CONFIG[file]);
 
 export default {
   name: 'dataadb',
   data() {
     return {
       STATES,
-      TRACE_FILES,
-      DUMP_FILES,
-      CAPTURE_FILES,
-      FILE_TYPES,
+      TRACES,
+      TRACE_CONFIG,
+      DUMPS,
+      FILE_DECODERS,
       WINSCOPE_PROXY_VERSION,
       status: STATES.CONNECTING,
       dataFiles: [],
@@ -168,14 +235,28 @@
       keep_alive_worker: null,
       errorText: '',
       loadProgress: 0,
-      adbStore: LocalStore('adb', Object.assign({
-        proxyKey: '',
-        lastDevice: '',
-      }, CAPTURE_FILES.reduce(function(obj, key) { obj[key] = true; return obj }, {}))),
+      adbStore: LocalStore(
+          'adb',
+          Object.assign(
+              {
+                proxyKey: '',
+                lastDevice: '',
+              },
+              Object.keys(TRACES)
+                  .concat(Object.keys(DUMPS))
+                  .concat(CONFIGS)
+                  .reduce(function(obj, key) {
+                    obj[key] = true; return obj;
+                  }, {}),
+          ),
+      ),
       downloadProxyUrl: 'https://android.googlesource.com/platform/development/+/master/tools/winscope/adb_proxy/winscope_proxy.py',
-    }
+    };
   },
-  props: ["store"],
+  props: ['store'],
+  components: {
+    'flat-card': FlatCard,
+  },
   methods: {
     getDevices() {
       if (this.status !== STATES.DEVICES && this.status !== STATES.CONNECTING) {
@@ -183,22 +264,23 @@
         this.refresh_worker = null;
         return;
       }
-      this.callProxy("GET", PROXY_ENDPOINTS.DEVICES, this, function(request, view) {
+      this.callProxy('GET', PROXY_ENDPOINTS.DEVICES, this, function(request, view) {
         try {
           view.devices = JSON.parse(request.responseText);
           if (view.adbStore.lastDevice && view.devices[view.adbStore.lastDevice] && view.devices[view.adbStore.lastDevice].authorised) {
-            view.selectDevice(view.adbStore.lastDevice)
+            view.selectDevice(view.adbStore.lastDevice);
           } else {
             if (view.refresh_worker === null) {
-              view.refresh_worker = setInterval(view.getDevices, 1000)
+              view.refresh_worker = setInterval(view.getDevices, 1000);
             }
             view.status = STATES.DEVICES;
           }
         } catch (err) {
+          console.error(err);
           view.errorText = request.responseText;
           view.status = STATES.ERROR;
         }
-      })
+      });
     },
     keepAliveTrace() {
       if (this.status !== STATES.END_TRACE) {
@@ -206,68 +288,95 @@
         this.keep_alive_worker = null;
         return;
       }
-      this.callProxy("GET", PROXY_ENDPOINTS.STATUS + this.deviceId() + "/", this, function(request, view) {
-        if (request.responseText !== "True") {
+      this.callProxy('GET', PROXY_ENDPOINTS.STATUS + this.deviceId() + '/', this, function(request, view) {
+        if (request.responseText !== 'True') {
           view.endTrace();
         } else if (view.keep_alive_worker === null) {
-          view.keep_alive_worker = setInterval(view.keepAliveTrace, 1000)
+          view.keep_alive_worker = setInterval(view.keepAliveTrace, 1000);
         }
-      })
+      });
     },
     startTrace() {
-      const requested = this.toTrace()
+      const requested = this.toTrace();
+      const requestedConfig = this.toTraceConfig();
       if (requested.length < 1) {
-        this.errorText = "No targets selected";
+        this.errorText = 'No targets selected';
         this.status = STATES.ERROR;
-        return
+        return;
       }
+      if (requestedConfig.length > 0) {
+        this.callProxy('POST', PROXY_ENDPOINTS.CONFIG_TRACE + this.deviceId() + '/', this, null, null, requestedConfig);
+      }
+
       this.status = STATES.END_TRACE;
-      this.callProxy("POST", PROXY_ENDPOINTS.START_TRACE + this.deviceId() + "/", this, function(request, view) {
+      this.callProxy('POST', PROXY_ENDPOINTS.START_TRACE + this.deviceId() + '/', this, function(request, view) {
         view.keepAliveTrace();
-      }, null, requested)
+      }, null, requested);
     },
     dumpState() {
-      const requested = this.toDump()
+      const requested = this.toDump();
       if (requested.length < 1) {
-        this.errorText = "No targets selected";
+        this.errorText = 'No targets selected';
         this.status = STATES.ERROR;
-        return
+        return;
       }
       this.status = STATES.LOAD_DATA;
-      this.callProxy("POST", PROXY_ENDPOINTS.DUMP + this.deviceId() + "/", this, function(request, view) {
+      this.callProxy('POST', PROXY_ENDPOINTS.DUMP + this.deviceId() + '/', this, function(request, view) {
         view.loadFile(requested, 0);
-      }, null, requested)
+      }, null, requested);
     },
     endTrace() {
       this.status = STATES.LOAD_DATA;
-      this.callProxy("POST", PROXY_ENDPOINTS.END_TRACE + this.deviceId() + "/", this, function(request, view) {
+      this.callProxy('POST', PROXY_ENDPOINTS.END_TRACE + this.deviceId() + '/', this, function(request, view) {
         view.loadFile(view.toTrace(), 0);
-      })
+      });
     },
     loadFile(files, idx) {
-      this.callProxy("GET", PROXY_ENDPOINTS.FETCH + this.deviceId() + "/" + files[idx] + "/", this, function(request, view) {
+      this.callProxy('GET', PROXY_ENDPOINTS.FETCH + this.deviceId() + '/' + files[idx] + '/', this, function(request, view) {
         try {
-          var buffer = new Uint8Array(request.response);
-          var filetype = FILE_TYPES[files[idx]];
-          var data = filetype.decoder(buffer, filetype, filetype.name, view.store);
-          view.dataFiles.push(data)
-          view.loadProgress = 100 * (idx + 1) / files.length;
+          const enc = new TextDecoder('utf-8');
+          const resp = enc.decode(request.response);
+          const filesByType = JSON.parse(resp);
+
+          for (const filetype in filesByType) {
+            if (filesByType.hasOwnProperty(filetype)) {
+              const files = filesByType[filetype];
+              const fileDecoder = FILE_DECODERS[proxyFileTypeAdapter[filetype]];
+
+              for (const encodedFileBuffer of files) {
+                const buffer = Uint8Array.from(atob(encodedFileBuffer), (c) => c.charCodeAt(0));
+                const data = fileDecoder.decoder(buffer, fileDecoder.decoderParams, fileDecoder.name, view.store);
+                view.dataFiles.push(data);
+                view.loadProgress = 100 * (idx + 1) / files.length; // TODO: Update this
+              }
+            }
+          }
+
           if (idx < files.length - 1) {
-            view.loadFile(files, idx + 1)
+            view.loadFile(files, idx + 1);
           } else {
             view.$emit('dataReady', view.dataFiles);
           }
         } catch (err) {
+          console.error(err);
           view.errorText = err;
           view.status = STATES.ERROR;
         }
-      }, "arraybuffer")
+      }, 'arraybuffer');
     },
     toTrace() {
-      return TRACE_FILES.filter(file => this.adbStore[file]);
+      return Object.keys(TRACES)
+          .filter((traceKey) => this.adbStore[traceKey]);
+    },
+    toTraceConfig() {
+      return Object.keys(TRACE_CONFIG)
+          .filter((file) => this.adbStore[file])
+          .flatMap((file) => TRACE_CONFIG[file])
+          .filter((config) => this.adbStore[config]);
     },
     toDump() {
-      return DUMP_FILES.filter(file => this.adbStore[file]);
+      return Object.keys(DUMPS)
+          .filter((dumpKey) => this.adbStore[dumpKey]);
     },
     selectDevice(device_id) {
       this.selectedDevice = device_id;
@@ -282,10 +391,10 @@
     },
     resetLastDevice() {
       this.adbStore.lastDevice = '';
-      this.restart()
+      this.restart();
     },
     callProxy(method, path, view, onSuccess, type, jsonRequest) {
-      var request = new XMLHttpRequest();
+      const request = new XMLHttpRequest();
       var view = this;
       request.onreadystatechange = function() {
         if (this.readyState !== 4) {
@@ -294,38 +403,38 @@
         if (this.status === 0) {
           view.status = STATES.NO_PROXY;
         } else if (this.status === 200) {
-          if (this.getResponseHeader("Winscope-Proxy-Version") !== WINSCOPE_PROXY_VERSION) {
+          if (this.getResponseHeader('Winscope-Proxy-Version') !== WINSCOPE_PROXY_VERSION) {
             view.status = STATES.INVALID_VERSION;
-          } else {
-            onSuccess(this, view)
+          } else if (onSuccess) {
+            onSuccess(this, view);
           }
         } else if (this.status === 403) {
           view.status = STATES.UNAUTH;
         } else {
-          if (this.responseType === "text" || !this.responseType) {
+          if (this.responseType === 'text' || !this.responseType) {
             view.errorText = this.responseText;
-          } else if (this.responseType === "arraybuffer") {
+          } else if (this.responseType === 'arraybuffer') {
             view.errorText = String.fromCharCode.apply(null, new Uint8Array(this.response));
           }
           view.status = STATES.ERROR;
         }
-      }
-      request.responseType = type || "";
+      };
+      request.responseType = type || '';
       request.open(method, WINSCOPE_PROXY_URL + path);
-      request.setRequestHeader("Winscope-Token", this.adbStore.proxyKey);
+      request.setRequestHeader('Winscope-Token', this.adbStore.proxyKey);
       if (jsonRequest) {
-        const json = JSON.stringify(jsonRequest)
-        request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
-        request.send(json)
+        const json = JSON.stringify(jsonRequest);
+        request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
+        request.send(json);
       } else {
         request.send();
       }
-    }
+    },
   },
   created() {
-    var urlParams = new URLSearchParams(window.location.search);
-    if (urlParams.has("token")) {
-      this.adbStore.proxyKey = urlParams.get("token")
+    const urlParams = new URLSearchParams(window.location.search);
+    if (urlParams.has('token')) {
+      this.adbStore.proxyKey = urlParams.get('token');
     }
     this.getDevices();
   },
@@ -335,9 +444,23 @@
         if (st == STATES.CONNECTING) {
           this.getDevices();
         }
-      }
-    }
+      },
+    },
   },
-}
+};
 
 </script>
+<style scoped>
+.device-choice {
+  display: inline-flex;
+}
+h3 {
+  margin-bottom: 0;
+}
+.trace-btn, .dump-btn {
+  margin-top: 0;
+}
+pre {
+  white-space: pre-wrap;
+}
+</style>
diff --git a/tools/winscope/src/DataFilter.vue b/tools/winscope/src/DataFilter.vue
deleted file mode 100644
index b675b5a..0000000
--- a/tools/winscope/src/DataFilter.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<template>
- <div class="bounds" v-if="visible">
-   <md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions"
-           placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" >
-       <md-option value="displayCreation, displayDeletion">Display</md-option>
-       <md-option value="powerModeUpdate">Power Mode</md-option>
-       <md-option value="surfaceCreation, surfaceDeletion">Surface</md-option>
-       <md-option value="transaction">Transaction</md-option>
-       <md-option value="vsyncEvent">vsync</md-option>
-       <md-option value="bufferUpdate">Buffer</md-option>
-   </md-select>
- </div>
-</template>
-<script>
-import { DATA_TYPES } from './decode.js'
-
-export default {
-  name: 'datafilter',
-  props: ['file'],
-  data() {
-    return {
-      rawData: this.file.data,
-      rawTimeline: this.file.timeline,
-      visibleTransactions: ["powerModeUpdate", "surfaceCreation, surfaceDeletion",
-                    "displayCreation, displayDeletion", "transaction"]
-    };
-  },
-  methods: {
-    updateFilter() {
-      this.file.data =
-              this.rawData.filter(x => this.visibleTransactions.includes(x.obj.increment));
-      this.file.timeline =
-              this.rawTimeline.filter(x => this.file.data.map(y => y.timestamp).includes(x));
-    },
-  },
-  computed: {
-    visible() {
-      return this.file.type == DATA_TYPES.TRANSACTION
-    },
-  }
-}
-</script>
-
-<style scoped>
-    .bounds {
-        margin: 1em;
-    }
-</style>
diff --git a/tools/winscope/src/DataInput.vue b/tools/winscope/src/DataInput.vue
index b68ebf4..bb07106 100644
--- a/tools/winscope/src/DataInput.vue
+++ b/tools/winscope/src/DataInput.vue
@@ -13,100 +13,521 @@
      limitations under the License.
 -->
 <template>
-    <md-card style="min-width: 50em">
-      <md-card-header>
-        <div class="md-title">Open files</div>
-      </md-card-header>
-      <md-card-content>
-        <md-list>
-          <md-list-item v-for="file in dataFiles" v-bind:key="file.filename">
-            <md-icon>{{file.type.icon}}</md-icon>
-            <span class="md-list-item-text">{{file.filename}} ({{file.type.name}})</span>
-            <md-button class="md-icon-button md-accent" @click="onRemoveFile(file.type.name)">
-              <md-icon>close</md-icon>
-            </md-button>
-          </md-list-item>
-        </md-list>
-        <div>
-          <md-checkbox v-model="store.displayDefaults">Show default properties
-            <md-tooltip md-direction="bottom">
-              If checked, shows the value of all properties.
-              Otherwise, hides all properties whose value is the default for its data type.
-            </md-tooltip>
-          </md-checkbox>
+  <flat-card style="min-width: 50em">
+    <md-card-header>
+      <div class="md-title">Open files</div>
+    </md-card-header>
+    <md-card-content>
+      <md-list>
+        <md-list-item v-for="file in dataFiles" v-bind:key="file.filename">
+          <md-icon>{{FILE_ICONS[file.type]}}</md-icon>
+          <span class="md-list-item-text">{{file.filename}} ({{file.type}})
+          </span>
+          <md-button
+            class="md-icon-button md-accent"
+            @click="onRemoveFile(file.type)"
+          >
+            <md-icon>close</md-icon>
+          </md-button>
+        </md-list-item>
+      </md-list>
+      <md-progress-spinner
+        :md-diameter="30"
+        :md-stroke="3"
+        md-mode="indeterminate"
+        v-show="loadingFiles"
+      />
+      <div>
+        <md-checkbox v-model="store.displayDefaults" class="md-primary">
+          Show default properties
+          <md-tooltip md-direction="bottom">
+            If checked, shows the value of all properties.
+            Otherwise, hides all properties whose value is the default for its
+            data type.
+          </md-tooltip>
+        </md-checkbox>
+      </div>
+      <div class="md-layout">
+        <div class="md-layout-item md-small-size-100">
+          <md-field>
+          <md-select v-model="fileType" id="file-type" placeholder="File type">
+            <md-option value="auto">Detect type</md-option>
+            <md-option value="bugreport">Bug Report (.zip)</md-option>
+            <md-option
+              :value="k" v-for="(v,k) in FILE_DECODERS"
+              v-bind:key="v.name">{{v.name}}
+            ></md-option>
+          </md-select>
+          </md-field>
         </div>
-        <div class="md-layout">
-          <div class="md-layout-item md-small-size-100">
-            <md-select v-model="fileType" id="file-type" placeholder="File type">
-              <md-option value="auto">Detect type</md-option>
-              <md-option :value="k" v-for="(v,k) in FILE_TYPES" v-bind:key="v.name">{{v.name}}</md-option>
-            </md-select>
-          </div>
-        </div>
-        <div class="md-layout md-gutter">
-          <input type="file" @change="onLoadFile" id="upload-file" v-show="false" />
-          <label class="md-button md-accent md-raised md-theme-default" for="upload-file">Add File</label>
-          <md-button v-if="dataReady" @click="onSubmit" class="md-button md-primary md-raised md-theme-default">Submit</md-button>
-        </div>
-      </md-card-content>
-    </md-card>
+      </div>
+      <div class="md-layout">
+        <input
+          type="file"
+          @change="onLoadFile"
+          ref="fileUpload"
+          v-show="false"
+          :multiple="fileType === 'auto'"
+        />
+        <md-button
+          class="md-primary md-theme-default"
+          @click="$refs.fileUpload.click()"
+        >
+          Add File
+        </md-button>
+        <md-button
+          v-if="dataReady"
+          @click="onSubmit"
+          class="md-button md-primary md-raised md-theme-default"
+        >
+          Submit
+        </md-button>
+      </div>
+    </md-card-content>
+
+    <md-snackbar
+      md-position="center"
+      :md-duration="Infinity"
+      :md-active.sync="showFetchingSnackbar"
+      md-persistent
+    >
+      <span>{{ fetchingSnackbarText }}</span>
+    </md-snackbar>
+
+    <md-snackbar
+      md-position="center"
+      :md-duration="snackbarDuration"
+      :md-active.sync="showSnackbar"
+      md-persistent
+    >
+      <span style="white-space: pre-line;">{{ snackbarText }}</span>
+      <div @click="hideSnackbarMessage()">
+        <md-button class="md-icon-button">
+          <md-icon style="color: white">close</md-icon>
+        </md-button>
+      </div>
+    </md-snackbar>
+  </flat-card>
 </template>
 <script>
-import { detectAndDecode, FILE_TYPES, DATA_TYPES } from './decode.js'
+import FlatCard from './components/FlatCard.vue';
+import JSZip from 'jszip';
+import {
+  detectAndDecode,
+  FILE_TYPES,
+  FILE_DECODERS,
+  FILE_ICONS,
+  UndetectableFileType,
+} from './decode.js';
+import {WebContentScriptMessageType} from './utils/consts';
 
 export default {
   name: 'datainput',
   data() {
     return {
       FILE_TYPES,
-      fileType: "auto",
+      FILE_DECODERS,
+      FILE_ICONS,
+      fileType: 'auto',
       dataFiles: {},
-    }
+      loadingFiles: false,
+      showFetchingSnackbar: false,
+      showSnackbar: false,
+      snackbarDuration: 3500,
+      snackbarText: '',
+      fetchingSnackbarText: 'Fetching files...',
+    };
   },
   props: ['store'],
+  created() {
+    // Attempt to load files from extension if present
+    this.loadFilesFromExtension();
+  },
   methods: {
+    showSnackbarMessage(message, duration) {
+      this.snackbarText = message;
+      this.snackbarDuration = duration;
+      this.showSnackbar = true;
+    },
+    hideSnackbarMessage() {
+      this.showSnackbar = false;
+    },
+    getFetchFilesLoadingAnimation() {
+      let frame = 0;
+      const fetchingStatusAnimation = () => {
+        frame++;
+        this.fetchingSnackbarText = `Fetching files${'.'.repeat(frame % 4)}`;
+      };
+      let interval = undefined;
+
+      return Object.freeze({
+        start: () => {
+          this.showFetchingSnackbar = true;
+          interval = setInterval(fetchingStatusAnimation, 500);
+        },
+        stop: () => {
+          this.showFetchingSnackbar = false;
+          clearInterval(interval);
+        },
+      });
+    },
+    /**
+     * Attempt to load files from the extension if present.
+     *
+     * If the source URL parameter is set to the extension it make a request
+     * to the extension to fetch the files from the extension.
+     */
+    loadFilesFromExtension() {
+      const urlParams = new URLSearchParams(window.location.search);
+      if (urlParams.get('source') === 'openFromExtension' && chrome) {
+        // Fetch files from extension
+        const androidBugToolExtensionId = 'mbbaofdfoekifkfpgehgffcpagbbjkmj';
+
+        const loading = this.getFetchFilesLoadingAnimation();
+        loading.start();
+
+        // Request to convert the blob object url "blob:chrome-extension://xxx"
+        // the chrome extension has to a web downloadable url "blob:http://xxx".
+        chrome.runtime.sendMessage(androidBugToolExtensionId, {
+          action: WebContentScriptMessageType.CONVERT_OBJECT_URL,
+        }, async (response) => {
+          switch (response.action) {
+            case WebContentScriptMessageType.CONVERT_OBJECT_URL_RESPONSE:
+              if (response.attachments?.length > 0) {
+                const filesBlobPromises = response.attachments
+                    .map(async (attachment) => {
+                      const fileQueryResponse =
+                        await fetch(attachment.objectUrl);
+                      const blob = await fileQueryResponse.blob();
+
+                      /**
+                       * Note: The blob's media type is not correct.
+                       * It is always set to "image/png".
+                       * Context: http://google3/javascript/closure/html/safeurl.js?g=0&l=256&rcl=273756987
+                       */
+
+                      // Clone blob to clear media type.
+                      const file = new Blob([blob]);
+                      file.name = attachment.name;
+
+                      return file;
+                    });
+
+                const files = await Promise.all(filesBlobPromises);
+
+                loading.stop();
+                this.processFiles(files);
+              } else {
+                const failureMessages = 'Got no attachements from extension...';
+                console.warn(failureMessages);
+                this.showSnackbarMessage(failureMessages, 3500);
+              }
+              break;
+
+            default:
+              loading.stop();
+              const failureMessages =
+                'Received unhandled response code from extension.';
+              console.warn(failureMessages);
+              this.showSnackbarMessage(failureMessages, 3500);
+          }
+        });
+      }
+    },
     onLoadFile(e) {
-      var type = this.fileType;
-      var files = event.target.files || event.dataTransfer.files;
-      var file = files[0];
-      if (!file) {
-        // No file selected.
+      const files = event.target.files || event.dataTransfer.files;
+      this.processFiles(files);
+    },
+    async processFiles(files) {
+      let error;
+      const decodedFiles = [];
+      for (const file of files) {
+        try {
+          this.loadingFiles = true;
+          this.showSnackbarMessage(`Loading ${file.name}`, Infinity);
+          const result = await this.addFile(file);
+          decodedFiles.push(...result);
+          this.hideSnackbarMessage();
+        } catch (e) {
+          this.showSnackbarMessage(
+              `Failed to load '${file.name}'...\n${e}`, 5000);
+          console.error(e);
+          error = e;
+          break;
+        } finally {
+          this.loadingFiles = false;
+        }
+      }
+
+      event.target.value = '';
+
+      if (error) {
         return;
       }
-      this.$emit('statusChange', file.name + " (loading)");
 
-      var reader = new FileReader();
-      reader.onload = (e) => {
-        var buffer = new Uint8Array(e.target.result);
-        try {
-          if (FILE_TYPES[type]) {
-            var filetype = FILE_TYPES[type];
-            var data = filetype.decoder(buffer, filetype, file.name, this.store);
-          } else {
-            var [filetype, data] = detectAndDecode(buffer, file.name, this.store);
-          }
-        } catch (ex) {
-          this.$emit('statusChange', this.filename + ': ' + ex);
-          return;
-        } finally {
-          event.target.value = ''
+      // TODO: Handle the fact that we can now have multiple files of type
+      // FILE_TYPES.TRANSACTION_EVENTS_TRACE
+
+      const decodedFileTypes = new Set(Object.keys(this.dataFiles));
+      // A file is overridden if a file of the same type is upload twice, as
+      // Winscope currently only support at most one file to each type
+      const overriddenFileTypes = new Set();
+      const overriddenFiles = {}; // filetype => array of files
+      for (const decodedFile of decodedFiles) {
+        const dataType = decodedFile.filetype;
+
+        if (decodedFileTypes.has(dataType)) {
+          overriddenFileTypes.add(dataType);
+          (overriddenFiles[dataType] = overriddenFiles[dataType] || [])
+              .push(this.dataFiles[dataType]);
+        }
+        decodedFileTypes.add(dataType);
+
+        this.$set(this.dataFiles,
+            dataType, decodedFile.data);
+      }
+
+      // TODO(b/169305853): Remove this once we have magic numbers or another
+      // way to detect the file type more reliably.
+      for (const dataType in overriddenFiles) {
+        if (overriddenFiles.hasOwnProperty(dataType)) {
+          const files = overriddenFiles[dataType];
+          files.push(this.dataFiles[dataType]);
+
+          const selectedFile =
+              this.getMostLikelyCandidateFile(dataType, files);
+          this.$set(this.dataFiles, dataType, selectedFile);
+
+          // Remove selected file from overriden list
+          const index = files.indexOf(selectedFile);
+          files.splice(index, 1);
+        }
+      }
+
+      if (overriddenFileTypes.size > 0) {
+        this.displayFilesOverridenWarning(overriddenFiles);
+      }
+    },
+
+    /**
+     * Gets the file that is most likely to be the actual file of that type out
+     * of all the candidateFiles. This is required because there are some file
+     * types that have no magic number and may lead to false positives when
+     * decoding in decode.js. (b/169305853)
+     * @param {string} dataType - The type of the candidate files.
+     * @param {files[]} candidateFiles - The list all the files detected to be
+     *                                   of type dataType, passed in the order
+     *                                   they are detected/uploaded in.
+     * @return {file} - the most likely candidate.
+     */
+    getMostLikelyCandidateFile(dataType, candidateFiles) {
+      const keyWordsByDataType = {
+        [FILE_TYPES.WINDOW_MANAGER_DUMP]: 'window',
+        [FILE_TYPES.SURFACE_FLINGER_DUMP]: 'surface',
+      };
+
+      if (
+        !candidateFiles ||
+        !candidateFiles.length ||
+        candidateFiles.length == 0
+      ) {
+        throw new Error('No candidate files provided');
+      }
+
+      if (!keyWordsByDataType.hasOwnProperty(dataType)) {
+        console.warn(`setMostLikelyCandidateFile doesn't know how to handle ` +
+            `candidates of dataType ${dataType} – setting last candidate as ` +
+            `target file.`);
+
+        // We want to return the last candidate file so that, we always override
+        // old uploaded files with once of the latest uploaded files.
+        return candidateFiles.slice(-1)[0];
+      }
+
+      for (const file of candidateFiles) {
+        if (file.filename
+            .toLowerCase().includes(keyWordsByDataType[dataType])) {
+          return file;
+        }
+      }
+
+      // We want to return the last candidate file so that, we always override
+      // old uploaded files with once of the latest uploaded files.
+      return candidateFiles.slice(-1)[0];
+    },
+
+    /**
+     * Display a snackbar warning that files have been overriden and any
+     * relavant additional information in the logs.
+     * @param {{string: file[]}} overriddenFiles - a mapping from data types to
+     * the files of the of that datatype tha have been overriden.
+     */
+    displayFilesOverridenWarning(overriddenFiles) {
+      const overriddenFileTypes = Object.keys(overriddenFiles);
+      const overriddenCount = Object.values(overriddenFiles)
+          .map((files) => files.length).reduce((length, next) => length + next);
+
+      if (overriddenFileTypes.length === 1 && overriddenCount === 1) {
+        const type = overriddenFileTypes.values().next().value;
+        const overriddenFile = overriddenFiles[type][0].filename;
+        const keptFile = this.dataFiles[type].filename;
+        const message =
+          `'${overriddenFile}' is conflicting with '${keptFile}'. ` +
+          `Only '${keptFile}' will be kept. If you wish to display ` +
+          `'${overriddenFile}', please upload it again with no other file ` +
+          `of the same type.`;
+
+        this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
+        console.warn(message);
+      } else {
+        const message = `Mutiple conflicting files have been uploaded. ` +
+          `${overriddenCount} files have been discarded. Please check the ` +
+          `developer console for more information.`;
+        this.showSnackbarMessage(`WARNING: ${message}`, Infinity);
+
+        const messageBuilder = [];
+        for (const type of overriddenFileTypes.values()) {
+          const keptFile = this.dataFiles[type].filename;
+          const overriddenFilesCount = overriddenFiles[type].length;
+
+          messageBuilder.push(`${overriddenFilesCount} file` +
+              `${overriddenFilesCount > 1 ? 's' : ''} of type ${type} ` +
+              `${overriddenFilesCount > 1 ? 'have' : 'has'} been ` +
+              `overridden. Only '${keptFile}' has been kept.`);
         }
 
-        this.$set(this.dataFiles, filetype.dataType.name, data);
-        this.$emit('statusChange', null);
+        messageBuilder.push('');
+        messageBuilder.push('Please reupload the specific files you want ' +
+          'to read (one of each type).');
+        messageBuilder.push('');
+
+        messageBuilder.push('===============DISCARDED FILES===============');
+
+        for (const type of overriddenFileTypes.values()) {
+          const discardedFiles = overriddenFiles[type];
+
+          messageBuilder.push(`The following files of type ${type} ` +
+            `have been discarded:`);
+          for (const discardedFile of discardedFiles) {
+            messageBuilder.push(`  - ${discardedFile.filename}`);
+          }
+          messageBuilder.push('');
+        }
+
+        console.warn(messageBuilder.join('\n'));
       }
-      reader.readAsArrayBuffer(files[0]);
+    },
+
+    getFileExtensions(file) {
+      const split = file.name.split('.');
+      if (split.length > 1) {
+        return split.pop();
+      }
+
+      return undefined;
+    },
+    async addFile(file) {
+      const decodedFiles = [];
+      const type = this.fileType;
+
+      const extension = this.getFileExtensions(file);
+
+      // extension === 'zip' is required on top of file.type ===
+      // 'application/zip' because when loaded from the extension the type is
+      // incorrect. See comment in loadFilesFromExtension() for more
+      // information.
+      if (type === 'bugreport' ||
+          (type === 'auto' && (extension === 'zip' ||
+            file.type === 'application/zip'))) {
+        const results = await this.decodeArchive(file);
+        decodedFiles.push(...results);
+      } else {
+        const decodedFile = await this.decodeFile(file);
+        decodedFiles.push(decodedFile);
+      }
+
+      return decodedFiles;
+    },
+    readFile(file) {
+      return new Promise((resolve, _) => {
+        const reader = new FileReader();
+        reader.onload = async (e) => {
+          const buffer = new Uint8Array(e.target.result);
+          resolve(buffer);
+        };
+        reader.readAsArrayBuffer(file);
+      });
+    },
+    async decodeFile(file) {
+      const buffer = await this.readFile(file);
+
+      let filetype = this.filetype;
+      let data;
+      if (filetype) {
+        const fileDecoder = FILE_DECODERS[filetype];
+        data = fileDecoder.decoder(
+            buffer, fileDecoder.decoderParams, file.name, this.store);
+      } else {
+        // Defaulting to auto — will attempt to detect file type
+        [filetype, data] = detectAndDecode(buffer, file.name, this.store);
+      }
+
+      return {filetype, data};
+    },
+    async decodeArchive(archive) {
+      const buffer = await this.readFile(archive);
+
+      const zip = new JSZip();
+      const content = await zip.loadAsync(buffer);
+
+      const decodedFiles = [];
+
+      for (const filename in content.files) {
+        if (content.files.hasOwnProperty(filename)) {
+          const file = content.files[filename];
+          if (file.dir) {
+            // Ignore directories
+            continue;
+          }
+
+          const fileBlob = await file.async('blob');
+          // Get only filename and remove rest of path
+          fileBlob.name = filename.split('/').slice(-1).pop();
+
+          try {
+            const decodedFile = await this.decodeFile(fileBlob);
+
+            decodedFiles.push(decodedFile);
+          } catch (e) {
+            if (!(e instanceof UndetectableFileType)) {
+              throw e;
+            }
+          }
+        }
+      }
+
+      if (decodedFiles.length == 0) {
+        throw new Error('No matching files found in archive', archive);
+      }
+
+      return decodedFiles;
     },
     onRemoveFile(typeName) {
       this.$delete(this.dataFiles, typeName);
     },
     onSubmit() {
-      this.$emit('dataReady', Object.keys(this.dataFiles).map(key => this.dataFiles[key]));
-    }
+      this.$emit('dataReady',
+          Object.keys(this.dataFiles).map((key) => this.dataFiles[key]));
+    },
   },
   computed: {
-    dataReady: function() { return Object.keys(this.dataFiles).length > 0 }
-  }
-}
+    dataReady: function() {
+      return Object.keys(this.dataFiles).length > 0;
+    },
+  },
+  components: {
+    'flat-card': FlatCard,
+  },
+};
 
 </script>
diff --git a/tools/winscope/src/DataView.vue b/tools/winscope/src/DataView.vue
index 40d1b48..3ad4c67 100644
--- a/tools/winscope/src/DataView.vue
+++ b/tools/winscope/src/DataView.vue
@@ -13,35 +13,76 @@
      limitations under the License.
 -->
 <template>
-  <md-card v-if="file">
-    <md-card-header>
-      <md-card-header-text>
-        <div class="md-title">
-          <md-icon>{{file.type.icon}}</md-icon> {{file.filename}}
-        </div>
-      </md-card-header-text>
-      <md-button :href="file.blobUrl" :download="file.filename" class="md-icon-button">
-        <md-icon>save_alt</md-icon>
-      </md-button>
-    </md-card-header>
-    <traceview v-if="isTrace" :store="store" :file="file" ref="view" />
-    <videoview v-if="isVideo" :file="file" ref="view" />
-    <logview v-if="isLog" :file="file" ref="view" />
-    <div v-if="!(isTrace || isVideo || isLog)">
-      <h1 class="bad">Unrecognized DataType</h1>
-    </div>
-  </md-card>
+  <div @click="onClick($event)">
+    <flat-card v-if="hasDataView(file)">
+      <md-card-header>
+        <md-card-header-text>
+          <div class="md-title">
+            <md-icon>{{ TRACE_ICONS[file.type] }}</md-icon>
+            {{file.type}}
+          </div>
+        </md-card-header-text>
+        <md-button
+          :href="file.blobUrl"
+          :download="file.type"
+          class="md-icon-button"
+        >
+          <md-icon>save_alt</md-icon>
+        </md-button>
+      </md-card-header>
+
+      <WindowManagerTraceView
+        v-if="showInWindowManagerTraceView(file)"
+        :store="store"
+        :file="file"
+        ref="view"
+      />
+      <SurfaceFlingerTraceView
+        v-else-if="showInSurfaceFlingerTraceView(file)"
+        :store="store"
+        :file="file"
+        ref="view"
+      />
+      <transactionsview
+        v-else-if="isTransactions(file)"
+        :trace="file"
+        ref="view"
+      />
+      <logview
+        v-else-if="isLog(file)"
+        :file="file"
+        ref="view"
+      />
+      <traceview
+        v-else-if="showInTraceView(file)"
+        :store="store"
+        :file="file"
+        ref="view"
+      />
+      <div v-else>
+        <h1 class="bad">Unrecognized DataType</h1>
+      </div>
+
+    </flat-card>
+  </div>
 </template>
 <script>
-import TraceView from './TraceView.vue'
-import VideoView from './VideoView.vue'
-import LogView from './LogView.vue'
-import { DATA_TYPES } from './decode.js'
+import TraceView from '@/TraceView.vue';
+import WindowManagerTraceView from '@/WindowManagerTraceView.vue';
+import SurfaceFlingerTraceView from '@/SurfaceFlingerTraceView.vue';
+import TransactionsView from '@/TransactionsView.vue';
+import LogView from '@/LogView.vue';
+import FileType from '@/mixins/FileType.js';
+import FlatCard from '@/components/FlatCard.vue';
+
+import {TRACE_ICONS} from '@/decode.js';
 
 export default {
   name: 'dataview',
   data() {
-    return {}
+    return {
+      TRACE_ICONS,
+    };
   },
   methods: {
     arrowUp() {
@@ -50,31 +91,23 @@
     arrowDown() {
       return this.$refs.view.arrowDown();
     },
+    onClick(e) {
+      // Pass click event to parent, so that click event handler can be attached
+      // to component.
+      this.$emit('click', e);
+    },
   },
   props: ['store', 'file'],
-  computed: {
-    isTrace() {
-      return this.file.type == DATA_TYPES.WINDOW_MANAGER ||
-          this.file.type == DATA_TYPES.SURFACE_FLINGER ||
-          this.file.type == DATA_TYPES.TRANSACTION ||
-          this.file.type == DATA_TYPES.WAYLAND ||
-          this.file.type == DATA_TYPES.SYSTEM_UI ||
-          this.file.type == DATA_TYPES.LAUNCHER
-    },
-    isVideo() {
-      return this.file.type == DATA_TYPES.SCREEN_RECORDING;
-    },
-    isLog() {
-      return this.file.type == DATA_TYPES.PROTO_LOG
-    }
-  },
+  mixins: [FileType],
   components: {
     'traceview': TraceView,
-    'videoview': VideoView,
+    'transactionsview': TransactionsView,
     'logview': LogView,
-  }
-}
-
+    'flat-card': FlatCard,
+    WindowManagerTraceView,
+    SurfaceFlingerTraceView,
+  },
+};
 </script>
 <style>
 .bad {
diff --git a/tools/winscope/src/DefaultTreeElement.vue b/tools/winscope/src/DefaultTreeElement.vue
new file mode 100644
index 0000000..1df8d17
--- /dev/null
+++ b/tools/winscope/src/DefaultTreeElement.vue
@@ -0,0 +1,103 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <span>
+    <span class="kind">{{item.kind}}</span>
+    <span v-if="item.kind && item.name">-</span>
+    <span
+      v-if="simplifyNames && item.shortName &&
+            item.shortName !== item.name"
+    >{{ item.shortName }} <!-- No line break on purpose -->
+      <md-tooltip
+        md-delay="300"
+        md-direction="top"
+        style="margin-bottom: -10px"
+      >
+        {{item.name}}
+      </md-tooltip>
+    </span>
+    <span v-else>{{ item.name }}</span>
+    <div
+      v-for="c in item.chips"
+      v-bind:key="c.long"
+      :title="c.long"
+      :class="chipClassForChip(c)"
+    >{{c.short}} <!-- No line break on purpose -->
+      <md-tooltip
+        md-delay="300"
+        md-direction="top"
+        style="margin-bottom: -10px"
+      >
+        {{c.long}}
+      </md-tooltip>
+    </div>
+  </span>
+</template>
+
+<script>
+export default {
+  name: 'DefaultTreeElement',
+  props: ['item', 'simplify-names'],
+  methods: {
+    chipClassForChip(c) {
+      return [
+        'tree-view-internal-chip',
+        'tree-view-chip',
+        'tree-view-chip' + '-' +
+          (c.type?.toString() || c.class?.toString() || 'default'),
+      ];
+    },
+  },
+};
+</script>
+
+<style scoped>
+.tree-view-internal-chip {
+  display: inline-block;
+}
+
+.tree-view-chip {
+  padding: 0 10px;
+  border-radius: 10px;
+  background-color: #aaa;
+  color: black;
+}
+
+.tree-view-chip.tree-view-chip-warn {
+  background-color: #ffaa6b;
+  color: black;
+}
+
+.tree-view-chip.tree-view-chip-error {
+  background-color: #ff6b6b;
+  color: black;
+}
+
+.tree-view-chip.tree-view-chip-gpu {
+  background-color: #00c853;
+  color: black;
+}
+
+.tree-view-chip.tree-view-chip-hwc {
+  background-color: #448aff;
+  color: black;
+}
+
+span {
+  overflow-wrap: break-word;
+  flex: 1 1 auto;
+  width: 0;
+}
+</style>
diff --git a/tools/winscope/src/DraggableDiv.vue b/tools/winscope/src/DraggableDiv.vue
new file mode 100644
index 0000000..adccaf4
--- /dev/null
+++ b/tools/winscope/src/DraggableDiv.vue
@@ -0,0 +1,229 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <div
+    class="draggable-container"
+    :style="{visibility: contentIsLoaded ? 'visible' : 'hidden'}"
+  >
+    <md-card class="draggable-card">
+      <div class="header" @mousedown="onHeaderMouseDown">
+        <md-icon class="drag-icon">
+          drag_indicator
+        </md-icon>
+        <slot name="header" />
+      </div>
+      <div class="content">
+        <slot name="main" ref="content"/>
+        <div class="resizer" v-show="resizeable" @mousedown="onResizerMouseDown"/>
+      </div>
+    </md-card>
+  </div>
+</template>
+<script>
+export default {
+  name: "DraggableDiv",
+  // If asyncLoad is enabled must call contentLoaded when content is ready
+  props: ['position', 'asyncLoad', 'resizeable'],
+  data() {
+    return {
+      positions: {
+        clientX: undefined,
+        clientY: undefined,
+        movementX: 0,
+        movementY: 0,
+      },
+      parentResizeObserver: null,
+      contentIsLoaded: false,
+      extraWidth: 0,
+      extraHeight: 0,
+    }
+  },
+  methods: {
+    onHeaderMouseDown(e) {
+      e.preventDefault();
+
+      this.initDragAction(e);
+    },
+    onResizerMouseDown(e) {
+      e.preventDefault();
+
+      this.startResize(e);
+    },
+    initDragAction(e) {
+      this.positions.clientX = e.clientX;
+      this.positions.clientY = e.clientY;
+      document.onmousemove = this.startDrag;
+      document.onmouseup = this.stopDrag;
+    },
+    startDrag(e) {
+      e.preventDefault();
+
+      this.positions.movementX = this.positions.clientX - e.clientX;
+      this.positions.movementY = this.positions.clientY - e.clientY;
+      this.positions.clientX = e.clientX;
+      this.positions.clientY = e.clientY;
+
+      const parentHeight = this.$el.parentElement.clientHeight;
+      const parentWidth = this.$el.parentElement.clientWidth;
+
+      const divHeight = this.$el.clientHeight;
+      const divWidth = this.$el.clientWidth;
+
+      let top = this.$el.offsetTop - this.positions.movementY;
+      if (top < 0) {
+        top = 0;
+      }
+      if (top + divHeight > parentHeight) {
+        top = parentHeight - divHeight;
+      }
+
+      let left = this.$el.offsetLeft - this.positions.movementX;
+      if (left < 0) {
+        left = 0;
+      }
+      if (left + divWidth > parentWidth) {
+        left = parentWidth - divWidth;
+      }
+
+      this.$el.style.top = top + 'px';
+      this.$el.style.left = left + 'px';
+    },
+    stopDrag() {
+      document.onmouseup = null;
+      document.onmousemove = null;
+    },
+    startResize(e) {
+      e.preventDefault();
+      this.startResizeX = e.clientX;
+      this.startResizeY = e.clientY;
+      document.onmousemove = this.resizing;
+      document.onmouseup = this.stopResize;
+      document.body.style.cursor = "nwse-resize";
+    },
+    resizing(e) {
+      let extraWidth = this.extraWidth + (e.clientX - this.startResizeX);
+      if (extraWidth < 0) {
+        extraWidth = 0;
+      }
+      this.$emit('requestExtraWidth', extraWidth);
+
+      let extraHeight = this.extraHeight + (e.clientY - this.startResizeY);
+      if (extraHeight < 0) {
+        extraHeight = 0;
+      }
+      this.$emit('requestExtraHeight', extraHeight);
+    },
+    stopResize(e) {
+      this.extraWidth += e.clientX - this.startResizeX;
+      if (this.extraWidth < 0) {
+        this.extraWidth = 0;
+      }
+      this.extraHeight +=  e.clientY - this.startResizeY;
+      if (this.extraHeight < 0) {
+        this.extraHeight = 0;
+      }
+      document.onmousemove = null;
+      document.onmouseup = null;
+      document.body.style.cursor = null;
+    },
+    onParentResize() {
+      const parentHeight = this.$el.parentElement.clientHeight;
+      const parentWidth = this.$el.parentElement.clientWidth;
+
+      const elHeight = this.$el.clientHeight;
+      const elWidth = this.$el.clientWidth;
+      const rect = this.$el.getBoundingClientRect();
+
+      const offsetBottom = parentHeight - (rect.y + elHeight);
+      if (offsetBottom < 0) {
+        this.$el.style.top = parseInt(this.$el.style.top) + offsetBottom + 'px';
+      }
+
+      const offsetRight = parentWidth - (rect.x + elWidth);
+      if (offsetRight < 0) {
+        this.$el.style.left = parseInt(this.$el.style.left) + offsetRight + 'px';
+      }
+    },
+    contentLoaded() {
+      // To be called if content is loaded async (eg: video), so that div may
+      // position itself correctly.
+
+      if (this.contentIsLoaded) {
+        return;
+      }
+
+      this.contentIsLoaded = true;
+      const margin = 15;
+
+      switch (this.position) {
+        case 'bottomLeft':
+          this.moveToBottomLeft(margin);
+          break;
+
+        default:
+          throw new Error('Unsupported starting position for DraggableDiv');
+      }
+    },
+    moveToBottomLeft(margin) {
+      margin = margin || 0;
+
+      const divHeight = this.$el.clientHeight;
+      const parentHeight = this.$el.parentElement.clientHeight;
+
+      this.$el.style.top = parentHeight - divHeight - margin + 'px';
+      this.$el.style.left = margin + 'px';
+    },
+  },
+  mounted() {
+    if (!this.asyncLoad) {
+      this.contentLoaded();
+    }
+
+    // Listen for changes in parent height to avoid element exiting visible view
+    this.parentResizeObserver = new ResizeObserver(this.onParentResize);
+
+    this.parentResizeObserver.observe(this.$el.parentElement);
+  },
+  destroyed() {
+    this.parentResizeObserver.unobserve(this.$el.parentElement);
+  },
+}
+</script>
+<style scoped>
+.draggable-container {
+  position: absolute;
+}
+
+.draggable-card {
+  margin: 0;
+}
+
+.header {
+  cursor: grab;
+  padding: 3px;
+}
+
+.resizer {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 0 0 15px 15px;
+  border-color: transparent transparent #ffffff transparent;
+  cursor: nwse-resize;
+}
+</style>
\ No newline at end of file
diff --git a/tools/winscope/src/LogEntry.vue b/tools/winscope/src/LogEntry.vue
new file mode 100644
index 0000000..90f41d7
--- /dev/null
+++ b/tools/winscope/src/LogEntry.vue
@@ -0,0 +1,188 @@
+<template>
+  <div
+    class="entry"
+    :class="[
+      {
+        'inactive': !source.occured,
+        'just-inactivated': source.justInactivated,
+      },
+      source.level.toLowerCase()
+    ]"
+  >
+    <div class="level-column">
+      <div>
+        <div class="icon" v-if="source.level.toLowerCase() === 'verbose'">
+          v
+        </div>
+        <i class="material-icons icon" v-else>
+          {{ levelIcons[source.level.toLowerCase()] }}
+        </i>
+        <md-tooltip md-direction="right" style="margin-left: -15px">
+          {{ source.level.toLowerCase() }}
+        </md-tooltip>
+      </div>
+    </div>
+    <div class="time-column">
+      <a @click="setTimelineTime(source.timestamp)" class="time-link">
+        {{source.time}}
+      </a>
+      <div
+        class="new-badge"
+        :style="{visibility: source.new ? 'visible' : 'hidden'} "
+      >
+        New
+      </div>
+    </div>
+    <div class="tag-column">{{source.tag}}</div>
+    <div class="at-column">{{source.at}}</div>
+    <div class="message-column">{{source.text}}</div>
+  </div>
+</template>
+
+<script>
+import {logLevel} from './utils/consts';
+
+export default {
+  name: 'logentry',
+  props: {
+    index: {
+      type: Number,
+    },
+    source: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {
+      levelIcons: {
+        [logLevel.INFO]: 'info_outline',
+        [logLevel.DEBUG]: 'help_outline',
+        [logLevel.VERBOSE]: 'assignment',
+        [logLevel.WARN]: 'warning',
+        [logLevel.ERROR]: 'error',
+        [logLevel.WTF]: 'bolt',
+      },
+    };
+  },
+  methods: {
+    setTimelineTime(timestamp) {
+      this.$store.dispatch('updateTimelineTime', timestamp);
+    },
+  },
+};
+</script>
+<style scoped>
+.level-column {
+  width: 2em;
+  display: inline-flex;
+}
+
+.level-column > div {
+  align-self: start;
+}
+
+.time-column {
+  display: inline-flex;
+  width: 13em;
+}
+
+.time-column .time-link {
+  width: 9em;
+}
+
+.tag-column {
+  width: 11em;
+  min-width: 11em;
+}
+
+.at-column {
+  width: 30em;
+  min-width: 30em;
+}
+
+.message-column {
+  min-width: 50em;
+  flex-grow: 1;
+  word-wrap: break-word;
+}
+
+.entry {
+  width: 100%;
+  display: inline-flex;
+}
+
+.entry:hover {
+  background: #f1f1f1;
+}
+
+.entry > div {
+  padding: 6px 10px;
+  border-bottom: 1px solid #f1f1f1;
+}
+
+a {
+  cursor: pointer;
+}
+
+.inactive {
+  color: gray;
+}
+
+.inactive a {
+  color: gray;
+}
+
+.just-inactivated {
+  background: #dee2e3;
+}
+
+.new-badge {
+  display: inline-block;
+  background: rgb(84, 139, 247);
+  border-radius: 3px;
+  color: white;
+  padding: 0 5px;
+  margin-left: 5px;
+  font-size: 10px;
+  align-self: flex-start;
+}
+
+.entry.warn, .entry.warn > div {
+  background: #FFE0B2;
+}
+
+.entry.warn.inactive, .entry.warn.inactive > div {
+  background: #FFF3E0;
+}
+
+.entry.error, .entry.error > div,
+.entry.wtf, .entry.wtf > div {
+  background: #FFCCBC;
+}
+
+.entry.error.inactive, .entry.error.inactive > div,
+.entry.wtf.inactive, .entry.wtf.inactive > div {
+  background: #FBE9E7;
+}
+
+.level-column .icon {
+  font-size: 15px;
+  color: gray;
+  width: 15px;
+  height: 15px;
+  text-align: center;
+}
+
+.entry.warn .level-column .icon {
+  color: #FBC02D;
+  font-size: 20px;
+}
+
+.entry.error .level-column .icon, .entry.wtf .level-column .icon  {
+  color: #FF6E40;
+  font-size: 20px;
+}
+</style>
diff --git a/tools/winscope/src/LogView.vue b/tools/winscope/src/LogView.vue
index bce7eb2..ce9ff10 100644
--- a/tools/winscope/src/LogView.vue
+++ b/tools/winscope/src/LogView.vue
@@ -14,31 +14,109 @@
 -->
 <template>
   <md-card-content class="container">
-    <md-table class="log-table">
-      <md-table-header>
-        <md-table-head class="time-column-header">Time</md-table-head>
-        <md-table-head class="tag-column-header">Tag</md-table-head>
-        <md-table-head class="at-column-header">At</md-table-head>
-        <md-table-head>Message</md-table-head>
-      </md-table-header>
-      <md-table-body>
-        <md-table-row v-for="line in data" :key="line.timestamp">
-          <md-table-cell class="time-column">{{line.time}}</md-table-cell>
-          <md-table-cell class="tag-column">{{line.tag}}</md-table-cell>
-          <md-table-cell class="at-column">{{line.at}}</md-table-cell>
-          <md-table-cell>{{line.text}}</md-table-cell>
-        </md-table-row>
-      </md-table-body>
-    </md-table>
+    <div class="navigation">
+      <md-button
+        class="md-dense md-primary"
+        @click.native="scrollToRow(lastOccuredVisibleIndex)"
+      >
+        Jump to latest entry
+      </md-button>
+      <md-button
+        class="md-icon-button" :class="{'md-primary': pinnedToLatest}"
+        @click.native="togglePin"
+      >
+        <md-icon>push_pin</md-icon>
+        <md-tooltip md-direction="top" v-if="pinnedToLatest">
+          Unpin to latest message
+        </md-tooltip>
+        <md-tooltip md-direction="top" v-else>
+          Pin to latest message
+        </md-tooltip>
+      </md-button>
+    </div>
+
+    <div class="filters">
+      <md-field>
+        <label>Log Levels</label>
+        <md-select v-model="selectedLogLevels" multiple>
+          <md-option v-for="level in logLevels" :value="level">{{ level }}</md-option>
+        </md-select>
+      </md-field>
+
+      <md-field>
+        <label>Tags</label>
+        <md-select v-model="selectedTags" multiple>
+          <md-option v-for="tag in tags" :value="tag">{{ tag }}</md-option>
+        </md-select>
+      </md-field>
+
+      <md-autocomplete v-model="selectedSourceFile" :md-options="sourceFiles">
+        <label>Source file</label>
+
+        <template slot="md-autocomplete-item" slot-scope="{ item, term }">
+          <md-highlight-text :md-term="term">{{ item }}</md-highlight-text>
+        </template>
+
+        <template slot="md-autocomplete-empty" slot-scope="{ term }">
+          No source file matching "{{ term }}" was found.
+        </template>
+      </md-autocomplete>
+
+      <md-field class="search-message-field" md-clearable>
+        <md-input placeholder="Search messages..." v-model="searchInput"></md-input>
+      </md-field>
+    </div>
+
+    <div v-if="processedData.length > 0" style="overflow-y: auto;">
+      <virtual-list style="height: 600px; overflow-y: auto;"
+        :data-key="'uid'"
+        :data-sources="processedData"
+        :data-component="logEntryComponent"
+        ref="loglist"
+      />
+    </div>
+    <div class="no-logs-message" v-else>
+      <md-icon>error_outline</md-icon>
+      <span class="message">No logs founds...</span>
+    </div>
   </md-card-content>
 </template>
 <script>
+import { findLastMatchingSorted } from './utils/utils.js';
+import { logLevel } from './utils/consts';
+import LogEntryComponent from './LogEntry.vue';
+import VirtualList from '../libs/virtualList/VirtualList';
+
 export default {
   name: 'logview',
   data() {
+    const data = this.file.data;
+
+    const tags = new Set();
+    const sourceFiles = new Set();
+    for (const line of data) {
+      tags.add(line.tag);
+      sourceFiles.add(line.at);
+    }
+
+    data.forEach((entry, index) => entry.index = index);
+
+    const logLevels = Object.values(logLevel);
+
     return {
-      data: [],
+      data,
       isSelected: false,
+      prevLastOccuredIndex: -1,
+      lastOccuredIndex: 0,
+      selectedTags: [],
+      selectedSourceFile: null,
+      searchInput: null,
+      sourceFiles: Object.freeze(Array.from(sourceFiles)),
+      tags: Object.freeze(Array.from(tags)),
+      pinnedToLatest: true,
+      logEntryComponent: LogEntryComponent,
+      logLevels,
+      selectedLogLevels: [],
     }
   },
   methods: {
@@ -49,90 +127,165 @@
     arrowDown() {
       this.isSelected = !this.isSelected;
       return !this.isSelected;
-    }
-  },
-  updated() {
-    let scrolltable = this.$el.getElementsByTagName("tbody")[0]
-    scrolltable.scrollTop = scrolltable.scrollHeight - 100;
+    },
+    getRowEl(idx) {
+      return this.$refs.tableBody.querySelectorAll('tr')[idx];
+    },
+    togglePin() {
+      this.pinnedToLatest = !this.pinnedToLatest;
+    },
+    scrollToRow(index) {
+      if (!this.$refs.loglist) {
+        return;
+      }
+
+      const itemOffset = this.$refs.loglist.virtual.getOffset(index);
+      const itemSize = 35;
+      const loglistSize = this.$refs.loglist.getClientSize();
+
+      this.$refs.loglist.scrollToOffset(itemOffset - loglistSize + itemSize);
+    },
+    getLastOccuredIndex(data, timestamp) {
+      if (this.data.length === 0) {
+          return 0;
+      }
+      return findLastMatchingSorted(data,
+        (array, idx) => array[idx].timestamp <= timestamp);
+    },
   },
   watch: {
-    selectedIndex: {
+    pinnedToLatest(isPinned) {
+      if (isPinned) {
+        this.scrollToRow(this.lastOccuredVisibleIndex);
+      }
+    },
+    currentTimestamp: {
       immediate: true,
-      handler(idx) {
-        if (this.file.data.length > 0) {
-          while (this.data.length > idx + 1) {
-            this.data.pop();
-          }
-          while (this.data.length <= idx) {
-            this.data.push(this.file.data[this.data.length]);
-          }
+      handler(newTimestamp) {
+        this.prevLastOccuredIndex = this.lastOccuredIndex;
+        this.lastOccuredIndex = this.getLastOccuredIndex(this.data, newTimestamp);
+
+        if (this.pinnedToLatest) {
+          this.scrollToRow(this.lastOccuredVisibleIndex);
         }
       },
     }
   },
   props: ['file'],
   computed: {
-    selectedIndex() {
-      return this.file.selectedIndex;
+    lastOccuredVisibleIndex() {
+      return this.getLastOccuredIndex(this.processedData, this.currentTimestamp);
     },
+    currentTimestamp() {
+      return this.$store.state.currentTimestamp;
+    },
+    processedData() {
+      const filteredData = this.data.filter(line => {
+        if (this.selectedLogLevels.length > 0 &&
+            !this.selectedLogLevels.includes(line.level.toLowerCase())) {
+          return false;
+        }
+
+        if (this.sourceFiles.includes(this.selectedSourceFile)) {
+          // Only filter once source file is fully inputed
+          if (line.at != this.selectedSourceFile) {
+            return false;
+          }
+        }
+
+        if (this.selectedTags.length > 0 && !this.selectedTags.includes(line.tag)) {
+          return false;
+        }
+
+        if (this.searchInput && !line.text.includes(this.searchInput)) {
+          return false;
+        }
+
+        return true;
+      });
+
+      for (const entry of filteredData) {
+        entry.new = this.prevLastOccuredIndex < entry.index &&
+          entry.index <= this.lastOccuredIndex;
+        entry.occured = entry.index <= this.lastOccuredIndex;
+        entry.justInactivated = this.lastOccuredIndex < entry.index &&
+          entry.index <= this.prevLastOccuredIndex;
+
+        // Force refresh if any of these changes
+        entry.uid = `${entry.index}${entry.new ? '-new' : ''}${entry.index}${entry.justInactivated ? '-just-inactivated' : ''}${entry.occured ? '-occured' : ''}`
+      }
+
+      return filteredData;
+    }
   },
+  components: {
+    'virtual-list': VirtualList,
+    'logentry': LogEntryComponent,
+  }
 }
 
 </script>
 <style>
-.log-table .md-table-cell {
-  height: auto;
+.container {
+  display: flex;
+  flex-wrap: wrap;
 }
 
-.log-table {
+.filters, .navigation {
   width: 100%;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
 }
 
-.time-column {
-  min-width: 15em;
+.navigation {
+  justify-content: flex-end;
 }
 
-.time-column-header {
-  min-width: 15em;
-  padding-right: 9em !important;
+.navigation > button {
+  margin: 0;
 }
 
-.tag-column {
-  min-width: 10em;
+.filters > div {
+  margin: 10px;
 }
 
-.tag-column-header {
-  min-width: 10em;
-  padding-right: 7em !important;
+.log-header {
+  display: inline-flex;
+  color: var(--md-theme-default-text-accent-on-background, rgba(0,0,0,0.54));
+  font-weight: bold;
 }
 
-.at-column {
-  min-width: 35em;
+.log-header > div {
+  padding: 6px 10px;
+  border-bottom: 1px solid #f1f1f1;
 }
 
-.at-column-header {
-  min-width: 35em;
-  padding-right: 32em !important;
+.log-header .time-column {
+  width: 13em;
 }
 
-.log-table table {
-  display: block;
+.log-header .tag-column {
+  width: 10em;
 }
 
-.log-table tbody {
-  display: block;
-  overflow-y: scroll;
-  width: 100%;
-  height: 20em;
+.log-header .at-column {
+  width: 30em;
 }
 
-.log-table tr {
-  width: 100%;
-  display: block;
+.column-title {
+  font-size: 12px;
 }
 
-.log-table td:last-child {
-  width: 100%;
+.no-logs-message {
+  margin: 15px;
+  display: flex;
+  align-content: center;
+  align-items: center;
 }
 
+.no-logs-message .message {
+  margin-left: 10px;
+  font-size: 15px;
+}
 </style>
diff --git a/tools/winscope/src/NodeContextMenu.vue b/tools/winscope/src/NodeContextMenu.vue
new file mode 100644
index 0000000..1e05639
--- /dev/null
+++ b/tools/winscope/src/NodeContextMenu.vue
@@ -0,0 +1,95 @@
+<template>
+  <vue-context ref="menu">
+    <li>
+      <a href="#" @click.prevent="$emit('collapseAllOtherNodes')">
+        Collapse all other nodes
+      </a>
+    </li>
+  </vue-context>
+</template>
+
+<script>
+import {VueContext} from 'vue-context';
+
+export default {
+  name: 'NodeContextMenu',
+  components: {
+    VueContext,
+  },
+  methods: {
+    open(e) {
+      this.$refs.menu.open(e);
+    },
+    close() {
+      this.$refs.menu.close();
+    },
+  },
+};
+</script>
+<style scoped>
+.v-context,
+.v-context ul {
+  background-color: #fff;
+  background-clip: padding-box;
+  border-radius: .25rem;
+  border: 1px solid rgba(0, 0, 0, .15);
+  box-shadow:
+    0 2px 2px 0 rgba(0, 0, 0, .14),
+    0 3px 1px -2px rgba(0, 0, 0, .2),
+    0 1px 5px 0 rgba(0, 0, 0, .12);
+  display: block;
+  margin: 0;
+  padding: 10px 0;
+  min-width: 10rem;
+  z-index: 1500;
+  position: fixed;
+  list-style: none;
+  box-sizing: border-box;
+  max-height: calc(100% - 50px);
+  overflow-y: auto
+}
+
+.v-context>li,
+.v-context ul>li {
+  margin: 0;
+  position: relative
+}
+
+.v-context>li>a,
+.v-context ul>li>a {
+  display: block;
+  padding: .5rem 1.5rem;
+  font-weight: 400;
+  color: #212529;
+  text-decoration: none;
+  white-space: nowrap;
+  background-color: transparent;
+  border: 0
+}
+
+.v-context>li>a:focus,
+.v-context>li>a:hover,
+.v-context ul>li>a:focus,
+.v-context ul>li>a:hover {
+  text-decoration: none;
+  color: #212529;
+  background-color: #f8f9fa
+}
+
+.v-context:focus,
+.v-context>li>a:focus,
+.v-context ul:focus,
+.v-context ul>li>a:focus {
+  outline: 0
+}
+
+.v-context__sub>a:after {
+  content: "\2BC8";
+  float: right;
+  padding-left: 1rem
+}
+
+.v-context__sub>ul {
+  display: none
+}
+</style>
diff --git a/tools/winscope/src/Overlay.vue b/tools/winscope/src/Overlay.vue
new file mode 100644
index 0000000..4bb051e
--- /dev/null
+++ b/tools/winscope/src/Overlay.vue
@@ -0,0 +1,837 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <div class="overlay" v-if="hasTimeline || video">
+    <div class="overlay-content" ref="overlayContent">
+      <draggable-div
+        ref="videoOverlay"
+        class="video-overlay"
+        v-show="minimized && showVideoOverlay"
+        position="bottomLeft"
+        :asyncLoad="true"
+        :resizeable="true"
+        v-on:requestExtraWidth="updateVideoOverlayWidth"
+        :style="videoOverlayStyle"
+        v-if="video"
+      >
+        <template slot="header">
+          <div class="close-video-overlay" @click="closeVideoOverlay">
+            <md-icon>
+              close
+              <md-tooltip md-direction="right">Close video overlay</md-tooltip>
+            </md-icon>
+          </div>
+        </template>
+        <template slot="main">
+          <div ref="overlayVideoContainer">
+            <videoview
+              ref="video"
+              :file="video"
+              :height="videoHeight"
+              @loaded="videoLoaded" />
+          </div>
+        </template>
+      </draggable-div>
+    </div>
+    <md-bottom-bar
+      class="bottom-nav"
+      v-if="hasTimeline || (video && !showVideoOverlay)"
+      ref="bottomNav"
+    >
+      <div class="nav-content">
+        <div class="">
+          <md-toolbar
+            md-elevation="0"
+            class="md-transparent">
+
+            <div class="toolbar" :class="{ expanded: expanded }">
+              <div class="resize-bar" v-show="expanded">
+                <div v-if="video" @mousedown="resizeBottomNav">
+                  <md-icon class="drag-handle">
+                    drag_handle
+                    <md-tooltip md-direction="top">resize</md-tooltip>
+                  </md-icon>
+                </div>
+              </div>
+
+              <div class="active-timeline" v-show="minimized">
+                <div
+                  class="active-timeline-icon"
+                  @click="$refs.navigationTypeSelection.$el
+                          .querySelector('input').click()"
+                >
+                  <md-icon class="collapsed-timeline-icon">
+                    {{ collapsedTimelineIcon }}
+                    <md-tooltip>
+                      {{ collapsedTimelineIconTooltip }}
+                    </md-tooltip>
+                  </md-icon>
+                </div>
+
+                <md-field
+                  ref="navigationTypeSelection"
+                  class="nagivation-style-selection-field"
+                >
+
+                  <label>Navigation</label>
+                  <md-select
+                    v-model="navigationStyle"
+                    name="navigationStyle"
+                    md-dense
+                  >
+                    <md-icon-option :value="NAVIGATION_STYLE.GLOBAL"
+                      icon="public"
+                      desc="Consider all timelines for navigation"
+                    />
+                    <md-icon-option
+                      :value="NAVIGATION_STYLE.FOCUSED"
+                      :icon="TRACE_ICONS[focusedFile.type]"
+                      :desc="`Automatically switch what timeline is considered
+                        for navigation based on what is visible on screen.
+                        Currently ${focusedFile.type}.`"
+                    />
+                    <!-- TODO: Add edit button for custom settings that opens
+                               popup dialog menu -->
+                    <md-icon-option
+                      :value="NAVIGATION_STYLE.CUSTOM"
+                      icon="dashboard_customize"
+                      desc="Considers only the enabled timelines for
+                            navigation. Expand the bottom bar to toggle
+                            timelines."
+                    />
+                    <md-optgroup label="Targeted">
+                      <md-icon-option
+                        v-for="file in timelineFiles"
+                        v-bind:key="file.type"
+                        :value="`${NAVIGATION_STYLE.TARGETED}-` +
+                                `${file.type}`"
+                        :displayValue="file.type"
+                        :shortValue="NAVIGATION_STYLE.TARGETED"
+                        :icon="TRACE_ICONS[file.type]"
+                        :desc="`Only consider ${file.type} ` +
+                               'for timeline navigation.'"
+                      />
+                    </md-optgroup>
+                  </md-select>
+                </md-field>
+              </div>
+
+              <div
+                class="minimized-timeline-content"
+                v-show="minimized"
+                v-if="hasTimeline"
+              >
+                <label>
+                  {{ seekTime }}
+                </label>
+                <timeline
+                  :timeline="Object.freeze(minimizedTimeline.timeline)"
+                  :selected-index="minimizedTimeline.selectedIndex"
+                  :scale="scale"
+                  :crop="crop"
+                  class="minimized-timeline"
+                />
+              </div>
+
+              <md-button
+                class="md-icon-button show-video-overlay-btn"
+                :class="{active: minimized && showVideoOverlay}"
+                @click="toggleVideoOverlay"
+                v-show="minimized"
+                style="margin-bottom: 10px;"
+              >
+                <i class="md-icon md-icon-font">
+                  featured_video
+                </i>
+                <md-tooltip md-direction="top">
+                  <span v-if="showVideoOverlay">Hide video overlay</span>
+                  <span v-else>Show video overlay</span>
+                </md-tooltip>
+              </md-button>
+
+              <md-button
+                class="md-icon-button toggle-btn"
+                @click="toggle"
+                style="margin-bottom: 10px;"
+              >
+                <md-icon v-if="minimized">
+                  expand_less
+                  <md-tooltip md-direction="top">Expand timeline</md-tooltip>
+                </md-icon>
+                <md-icon v-else>
+                  expand_more
+                  <md-tooltip md-direction="top">Collapse timeline</md-tooltip>
+                </md-icon>
+              </md-button>
+            </div>
+          </md-toolbar>
+
+          <div class="expanded-content" v-show="expanded">
+            <div :v-if="video">
+              <div
+                class="expanded-content-video"
+                ref="expandedContentVideoContainer"
+              >
+                <!-- Video moved here on expansion -->
+              </div>
+            </div>
+            <div class="flex-fill">
+              <div
+                ref="expandedTimeline"
+                :style="`padding-top: ${resizeOffset}px;`"
+              >
+                <div class="seek-time" v-if="seekTime">
+                  <b>Seek time</b>: {{ seekTime }}
+                </div>
+
+                <timelines
+                  :timelineFiles="timelineFiles"
+                  :scale="scale"
+                  :crop="crop"
+                  :cropIntent="cropIntent"
+                  v-on:crop="onTimelineCrop"
+                />
+
+                <div class="timeline-selection">
+                  <div class="timeline-selection-header">
+                    <label>Timeline Area Selection</label>
+                    <span class="material-icons help-icon">
+                      help_outline
+                      <md-tooltip md-direction="right">
+                        Select the area of the timeline to focus on.
+                        Click and drag to select.
+                      </md-tooltip>
+                    </span>
+                    <md-button
+                      class="md-primary"
+                      v-if="isCropped"
+                      @click.native="clearSelection"
+                    >
+                      Clear selection
+                    </md-button>
+                  </div>
+                  <timeline-selection
+                    :timeline="mergedTimeline.timeline"
+                    :start-timestamp="0"
+                    :end-timestamp="0"
+                    :scale="scale"
+                    :cropArea="crop"
+                    v-on:crop="onTimelineCrop"
+                    v-on:cropIntent="onTimelineCropIntent"
+                    v-on:showVideoAt="changeVideoTimestamp"
+                    v-on:resetVideoTimestamp="resetVideoTimestamp"
+                  />
+                </div>
+
+                <div class="help" v-if="!minimized">
+                  <div class="help-icon-wrapper">
+                    <span class="material-icons help-icon">
+                      help_outline
+                      <md-tooltip md-direction="left">
+                        Click on icons to disable timelines
+                      </md-tooltip>
+                    </span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </md-bottom-bar>
+  </div>
+</template>
+<script>
+import Timeline from './Timeline.vue';
+import Timelines from './Timelines.vue';
+import TimelineSelection from './TimelineSelection.vue';
+import DraggableDiv from './DraggableDiv.vue';
+import VideoView from './VideoView.vue';
+import MdIconOption from './components/IconSelection/IconSelectOption.vue';
+import FileType from './mixins/FileType.js';
+import {NAVIGATION_STYLE} from './utils/consts';
+import {TRACE_ICONS} from '@/decode.js';
+
+// eslint-disable-next-line camelcase
+import {nanos_to_string} from './transform.js';
+
+export default {
+  name: 'overlay',
+  props: ['store'],
+  mixins: [FileType],
+  data() {
+    return {
+      minimized: true,
+      // height of video in expanded timeline,
+      // made to match expandedTimeline dynamically
+      videoHeight: 'auto',
+      dragState: {
+        clientY: null,
+        lastDragEndPosition: null,
+      },
+      resizeOffset: 0,
+      showVideoOverlay: true,
+      mergedTimeline: null,
+      NAVIGATION_STYLE,
+      navigationStyle: this.store.navigationStyle,
+      videoOverlayExtraWidth: 0,
+      crop: null,
+      cropIntent: null,
+      TRACE_ICONS,
+    };
+  },
+  created() {
+    this.mergedTimeline = this.computeMergedTimeline();
+    this.$store.commit('setMergedTimeline', this.mergedTimeline);
+    this.updateNavigationFileFilter();
+  },
+  mounted() {
+    this.emitBottomHeightUpdate();
+  },
+  destroyed() {
+    this.$store.commit('removeMergedTimeline', this.mergedTimeline);
+  },
+  watch: {
+    navigationStyle(style) {
+      // Only store navigation type in local store if it's a type that will
+      // work regardless of what data is loaded.
+      if (style === NAVIGATION_STYLE.GLOBAL ||
+        style === NAVIGATION_STYLE.FOCUSED) {
+        this.store.navigationStyle = style;
+      }
+      this.updateNavigationFileFilter();
+    },
+    minimized() {
+      // Minimized toggled
+      this.updateNavigationFileFilter();
+
+      this.$nextTick(this.emitBottomHeightUpdate);
+    },
+  },
+  computed: {
+    video() {
+      return this.$store.getters.video;
+    },
+    videoOverlayStyle() {
+      return {
+        width: 150 + this.videoOverlayExtraWidth + 'px',
+      };
+    },
+    timelineFiles() {
+      return this.$store.getters.timelineFiles;
+    },
+    focusedFile() {
+      return this.$store.state.focusedFile;
+    },
+    expanded() {
+      return !this.minimized;
+    },
+    seekTime() {
+      return nanos_to_string(this.currentTimestamp);
+    },
+    scale() {
+      const mx = Math.max(...(this.timelineFiles.map((f) =>
+        Math.max(...f.timeline))));
+      const mi = Math.min(...(this.timelineFiles.map((f) =>
+        Math.min(...f.timeline))));
+      return [mi, mx];
+    },
+    currentTimestamp() {
+      return this.$store.state.currentTimestamp;
+    },
+    hasTimeline() {
+      // Returns true if a meaningful timeline exists (i.e. not only dumps)
+      for (const file of this.timelineFiles) {
+        if (file.timeline.length > 0 &&
+            (file.timeline[0] !== undefined || file.timeline.length > 1)) {
+          return true;
+        }
+      }
+
+      return false;
+    },
+    collapsedTimelineIconTooltip() {
+      switch (this.navigationStyle) {
+        case NAVIGATION_STYLE.GLOBAL:
+          return 'All timelines';
+
+        case NAVIGATION_STYLE.FOCUSED:
+          return `Focused: ${this.focusedFile.type}`;
+
+        case NAVIGATION_STYLE.CUSTOM:
+          return 'Enabled timelines';
+
+        default:
+          const split = this.navigationStyle.split('-');
+          if (split[0] !== NAVIGATION_STYLE.TARGETED) {
+            throw new Error('Unexpected nagivation type');
+          }
+
+          const fileType = split[1];
+
+          return fileType;
+      }
+    },
+    collapsedTimelineIcon() {
+      switch (this.navigationStyle) {
+        case NAVIGATION_STYLE.GLOBAL:
+          return 'public';
+
+        case NAVIGATION_STYLE.FOCUSED:
+          return TRACE_ICONS[this.focusedFile.type];
+
+        case NAVIGATION_STYLE.CUSTOM:
+          return 'dashboard_customize';
+
+        default:
+          const split = this.navigationStyle.split('-');
+          if (split[0] !== NAVIGATION_STYLE.TARGETED) {
+            throw new Error('Unexpected nagivation type');
+          }
+
+          const fileType = split[1];
+
+          return TRACE_ICONS[fileType];
+      }
+    },
+    minimizedTimeline() {
+      if (this.navigationStyle === NAVIGATION_STYLE.GLOBAL) {
+        return this.mergedTimeline;
+      }
+
+      if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) {
+        return this.focusedFile;
+      }
+
+      if (this.navigationStyle === NAVIGATION_STYLE.CUSTOM) {
+        // TODO: Return custom timeline
+        return this.mergedTimeline;
+      }
+
+      if (this.navigationStyle.split('-')[0] === NAVIGATION_STYLE.TARGETED) {
+        return this.$store.state
+            .traces[this.navigationStyle.split('-')[1]];
+      }
+
+      throw new Error('Unexpected Nagivation Style');
+    },
+    isCropped() {
+      return this.crop != null &&
+        (this.crop.left !== 0 || this.crop.right !== 1);
+    },
+  },
+  updated() {
+    this.$nextTick(() => {
+      if (this.$refs.expandedTimeline && this.expanded) {
+        this.videoHeight = this.$refs.expandedTimeline.clientHeight;
+      } else {
+        this.videoHeight = 'auto';
+      }
+    });
+  },
+  methods: {
+    emitBottomHeightUpdate() {
+      if (this.$refs.bottomNav) {
+        const newHeight = this.$refs.bottomNav.$el.clientHeight;
+        this.$emit('bottom-nav-height-change', newHeight);
+      }
+    },
+    computeMergedTimeline() {
+      const mergedTimeline = {
+        timeline: [], // Array of integers timestamps
+        selectedIndex: 0,
+      };
+
+      const timelineIndexes = [];
+      const timelines = [];
+      for (const file of this.timelineFiles) {
+        timelineIndexes.push(0);
+        timelines.push(file.timeline);
+      }
+
+      while (true) {
+        let minTime = Infinity;
+        let timelineToAdvance;
+
+        for (let i = 0; i < timelines.length; i++) {
+          const timeline = timelines[i];
+          const index = timelineIndexes[i];
+
+          if (index >= timeline.length) {
+            continue;
+          }
+
+          const time = timeline[index];
+
+          if (time < minTime) {
+            minTime = time;
+            timelineToAdvance = i;
+          }
+        }
+
+        if (timelineToAdvance === undefined) {
+          // No more elements left
+          break;
+        }
+
+        timelineIndexes[timelineToAdvance]++;
+        mergedTimeline.timeline.push(minTime);
+      }
+
+      // Object is frozen for performance reasons
+      // It will prevent Vue from making it a reactive object which will be very
+      // slow as the timeline gets larger.
+      Object.freeze(mergedTimeline.timeline);
+
+      return mergedTimeline;
+    },
+    toggle() {
+      this.minimized ? this.expand() : this.minimize();
+
+      this.minimized = !this.minimized;
+    },
+    expand() {
+      if (this.video) {
+        this.$refs.expandedContentVideoContainer
+            .appendChild(this.$refs.video.$el);
+      }
+    },
+    minimize() {
+      if (this.video) {
+        this.$refs.overlayVideoContainer.appendChild(this.$refs.video.$el);
+      }
+    },
+    fileIsVisible(f) {
+      return this.visibleDataViews.includes(f.filename);
+    },
+    resizeBottomNav(e) {
+      this.initResizeAction(e);
+    },
+    initResizeAction(e) {
+      document.onmousemove = this.startResize;
+      document.onmouseup = this.endResize;
+    },
+    startResize(e) {
+      if (this.dragState.clientY === null) {
+        this.dragState.clientY = e.clientY;
+      }
+
+      const movement = this.dragState.clientY - e.clientY;
+
+      const resizeOffset = this.resizeOffset + movement;
+      if (resizeOffset < 0) {
+        this.resizeOffset = 0;
+        this.dragState.clientY = null;
+      } else if (movement > this.getBottomNavDistanceToTop()) {
+        this.dragState.clientY += this.getBottomNavDistanceToTop();
+        this.resizeOffset += this.getBottomNavDistanceToTop();
+      } else {
+        this.resizeOffset = resizeOffset;
+        this.dragState.clientY = e.clientY;
+      }
+    },
+    endResize() {
+      this.dragState.lastDragEndPosition = this.dragState.clientY;
+      this.dragState.clientY = null;
+      document.onmouseup = null;
+      document.onmousemove = null;
+    },
+    getBottomNavDistanceToTop() {
+      return this.$refs.bottomNav.$el.getBoundingClientRect().top;
+    },
+    closeVideoOverlay() {
+      this.showVideoOverlay = false;
+    },
+    openVideoOverlay() {
+      this.showVideoOverlay = true;
+    },
+    toggleVideoOverlay() {
+      this.showVideoOverlay = !this.showVideoOverlay;
+    },
+    videoLoaded() {
+      this.$refs.videoOverlay.contentLoaded();
+    },
+    updateNavigationFileFilter() {
+      if (!this.minimized) {
+        // Always use custom mode navigation when timeline is expanded
+        this.$store.commit('setNavigationFilesFilter',
+            (f) => !f.timelineDisabled);
+        return;
+      }
+
+      let navigationStyleFilter;
+      switch (this.navigationStyle) {
+        case NAVIGATION_STYLE.GLOBAL:
+          navigationStyleFilter = (f) => true;
+          break;
+
+        case NAVIGATION_STYLE.FOCUSED:
+          navigationStyleFilter =
+            (f) => f.type === this.focusedFile.type;
+          break;
+
+        case NAVIGATION_STYLE.CUSTOM:
+          navigationStyleFilter = (f) => !f.timelineDisabled;
+          break;
+
+        default:
+          const split = this.navigationStyle.split('-');
+          if (split[0] !== NAVIGATION_STYLE.TARGETED) {
+            throw new Error('Unexpected nagivation type');
+          }
+
+          const fileType = split[1];
+          navigationStyleFilter =
+            (f) => f.type === fileType;
+      }
+
+      this.$store.commit('setNavigationFilesFilter', navigationStyleFilter);
+    },
+    updateVideoOverlayWidth(width) {
+      this.videoOverlayExtraWidth = width;
+    },
+    onTimelineCrop(cropDetails) {
+      this.crop = cropDetails;
+    },
+    onTimelineCropIntent(cropIntent) {
+      this.cropIntent = cropIntent;
+    },
+    changeVideoTimestamp(ts) {
+      if (!this.$refs.video) {
+        return;
+      }
+      this.$refs.video.selectFrameAtTime(ts);
+    },
+    resetVideoTimestamp() {
+      if (!this.$refs.video) {
+        return;
+      }
+      this.$refs.video.jumpToSelectedIndex();
+    },
+    clearSelection() {
+      this.crop = null;
+    },
+  },
+  components: {
+    'timeline': Timeline,
+    'timelines': Timelines,
+    'timeline-selection': TimelineSelection,
+    'videoview': VideoView,
+    'draggable-div': DraggableDiv,
+    'md-icon-option': MdIconOption,
+  },
+};
+</script>
+<style scoped>
+.overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 10;
+  margin: 0;
+  display: flex;
+  flex-direction: column;
+  pointer-events: none;
+}
+
+.overlay-content {
+  flex-grow: 1;
+}
+
+.bottom-nav {
+  background: white;
+  margin: 0;
+  max-height: 100vh;
+  bottom: 0;
+  left: 0;
+  pointer-events: all;
+}
+
+.nav-content {
+  width: 100%;
+}
+
+.toolbar, .active-timeline, .options {
+  display: flex;
+  flex-direction: row;
+  flex: 1;
+  align-items: center;
+}
+
+.toolbar.expanded {
+  align-items: baseline;
+}
+
+.minimized-timeline-content {
+ flex-grow: 1;
+}
+
+.minimized-timeline-content .seek-time {
+  padding: 3px 0;
+}
+
+.options, .expanded-content .seek-time {
+  padding: 0 20px 15px 20px;
+}
+
+.options label {
+  font-weight: 600;
+}
+
+.options .datafilter {
+  height: 50px;
+  display: flex;
+  align-items: center;
+}
+
+.expanded-content {
+  display: flex;
+}
+
+.flex-fill {
+  flex-grow: 1;
+}
+
+.video {
+  flex-grow: 0;
+}
+
+.resize-bar {
+  flex-grow: 1;
+}
+
+.drag-handle {
+  cursor: grab;
+}
+
+.md-icon-button {
+  margin: 0;
+}
+
+.toggle-btn {
+  margin-left: 8px;
+  align-self: flex-end;
+}
+
+.video-overlay {
+  display: inline-block;
+  margin-bottom: 15px;
+  min-width: 50px;
+  max-width: 50vw;
+  height: auto;
+  resize: horizontal;
+  pointer-events: all;
+}
+
+.close-video-overlay {
+  float: right;
+  cursor: pointer;
+}
+
+.show-video-overlay-btn {
+  margin-left: 12px;
+  margin-right: -8px;
+  align-self: flex-end;
+}
+
+.show-video-overlay-btn .md-icon {
+  color: #9E9E9E!important;
+}
+
+.collapsed-timeline-icon {
+  cursor: pointer;
+}
+
+.show-video-overlay-btn.active .md-icon {
+  color: #212121!important;
+}
+
+.help {
+  display: flex;
+  align-content: flex-end;
+  align-items: flex-end;
+  flex-direction: column;
+}
+
+.help-icon-wrapper {
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+
+.help-icon-wrapper .help-icon {
+  cursor: help;
+}
+
+.trace-icon {
+  cursor: pointer;
+  user-select: none;
+}
+
+.trace-icon.disabled {
+  color: gray;
+}
+
+.active-timeline {
+  flex: 0 0 auto;
+}
+
+.active-timeline .icon {
+  margin-right: 20px;
+}
+
+.active-timeline .active-timeline-icon {
+  margin-right: 10px;
+  align-self: flex-end;
+  margin-bottom: 18px;
+}
+
+.minimized-timeline-content {
+  align-self: flex-start;
+  padding-top: 1px;
+}
+
+.minimized-timeline-content label {
+  color: rgba(0,0,0,0.54);
+  font-size: 12px;
+  font-family: inherit;
+}
+
+.minimized-timeline-content .minimized-timeline {
+  margin-top: 4px;
+}
+
+.nagivation-style-selection-field {
+  width: 90px;
+  margin-right: 10px;
+  margin-bottom: 0;
+}
+
+.timeline-selection-header {
+  display: flex;
+  align-items: center;
+  padding-left: 15px;
+  height: 48px;
+}
+
+.help-icon {
+  font-size: 15px;
+  margin-bottom: 15px;
+  cursor: help;
+}
+</style>
diff --git a/tools/winscope/src/PropertiesTreeElement.vue b/tools/winscope/src/PropertiesTreeElement.vue
new file mode 100644
index 0000000..07293f9
--- /dev/null
+++ b/tools/winscope/src/PropertiesTreeElement.vue
@@ -0,0 +1,84 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <span>
+    <span class="key">{{ key }} </span>
+    <span v-if="value">: </span>
+    <span class="value" v-if="value" :class="[valueClass]">{{ value }}</span>
+  </span>
+</template>
+<script>
+export default {
+  name: 'PropertiesTreeElement',
+  props: ['item', 'simplify-names'],
+  computed: {
+    key() {
+      if (!this.item.children || this.item.children.length === 0) {
+        return this.item.name.split(': ')[0];
+      }
+
+      return this.item.name;
+    },
+    value() {
+      if (!this.item.children || this.item.children.length === 0) {
+        return this.item.name.split(': ').slice(1).join(': ');
+      }
+
+      return null;
+    },
+    valueClass() {
+      if (!this.value) {
+        return null;
+      }
+
+      if (this.value == 'null') {
+        return 'null';
+      }
+
+      if (this.value == 'true') {
+        return 'true';
+      }
+
+      if (this.value == 'false') {
+        return 'false';
+      }
+
+      if (!isNaN(this.value)) {
+        return 'number';
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.key {
+  color: #4b4b4b;
+}
+.value {
+  color: #8A2BE2;
+}
+.value.null {
+  color: #e1e1e1;
+}
+.value.number {
+  color: #4c75fd;
+}
+.value.true {
+  color: #2ECC40;
+}
+.value.false {
+  color: #FF4136;
+}
+</style>
diff --git a/tools/winscope/src/Rects.vue b/tools/winscope/src/Rects.vue
index 9548d7e..4b732d7 100644
--- a/tools/winscope/src/Rects.vue
+++ b/tools/winscope/src/Rects.vue
@@ -14,8 +14,12 @@
 -->
 <template>
   <div class="bounds" :style="boundsStyle">
-    <div class="rect" v-for="r in rects" :style="rectToStyle(r)"
-        @click="onClick(r)">
+    <div
+      class="rect" v-for="r in filteredRects"
+      :style="rectToStyle(r)"
+      @click="onClick(r)"
+      v-bind:key="`${r.left}-${r.right}-${r.top}-${r.bottom}-${r.ref.name}`"
+    >
       <span class="label">{{r.label}}</span>
     </div>
     <div class="highlight" v-if="highlight" :style="rectToStyle(highlight)" />
@@ -24,13 +28,15 @@
 
 <script>
 
-import { multiply_rect } from './matrix_utils.js'
+// eslint-disable-next-line camelcase
+import {multiply_rect} from './matrix_utils.js';
 
 export default {
   name: 'rects',
   props: ['bounds', 'rects', 'highlight'],
-  data () {
+  data() {
     return {
+      desiredHeight: 800,
       desiredWidth: 400,
     };
   },
@@ -39,34 +45,46 @@
       if (this.bounds) {
         return this.bounds;
       }
-      var width = Math.max(...this.rects.map((r) => multiply_rect(r.transform, r).right));
-      var height = Math.max(...this.rects.map((r) => multiply_rect(r.transform, r).bottom));
+      const width = Math.max(
+          ...this.rects.map((r) => multiply_rect(r.transform, r).right));
+      const height = Math.max(
+          ...this.rects.map((r) => multiply_rect(r.transform, r).bottom));
       return {width, height};
     },
     boundsStyle() {
       return this.rectToStyle({top: 0, left: 0, right: this.boundsC.width,
-          bottom: this.boundsC.height});
+        bottom: this.boundsC.height});
+    },
+    filteredRects() {
+      return this.rects.filter((rect) => rect.ref.visible);
     },
   },
   methods: {
-    s(sourceCoordinate) {  // translate source into target coordinates
-      return sourceCoordinate / this.boundsC.width * this.desiredWidth;
+    s(sourceCoordinate) { // translate source into target coordinates
+      let scale;
+      if (this.boundsC.width < this.boundsC.height) {
+        scale = this.desiredHeight / this.boundsC.height;
+      } else {
+        scale = this.desiredWidth / this.boundsC.width;
+      }
+      return sourceCoordinate * scale;
     },
     rectToStyle(r) {
-      var x = this.s(r.left);
-      var y = this.s(r.top);
-      var w = this.s(r.right) - this.s(r.left);
-      var h = this.s(r.bottom) - this.s(r.top);
-      var t = r.transform;
-      var tr = t ? `matrix(${t.dsdx}, ${t.dtdx}, ${t.dsdy}, ${t.dtdy}, ${this.s(t.tx)}, ${this.s(t.ty)})` : '';
+      const x = this.s(r.left);
+      const y = this.s(r.top);
+      const w = this.s(r.right) - this.s(r.left);
+      const h = this.s(r.bottom) - this.s(r.top);
+      const t = r.transform;
+      const tr = t ? `matrix(${t.dsdx}, ${t.dtdx}, ${t.dsdy}, ${t.dtdy}, ` +
+          `${this.s(t.tx)}, ${this.s(t.ty)})` : '';
       return `top: ${y}px; left: ${x}px; height: ${h}px; width: ${w}px;` +
-             `transform: ${tr}; transform-origin: 0 0;`
+             `transform: ${tr}; transform-origin: 0 0;`;
     },
     onClick(r) {
       this.$emit('rect-click', r.ref);
     },
-  }
-}
+  },
+};
 </script>
 
 <style scoped>
@@ -82,10 +100,10 @@
 }
 .rect {
   border: 1px solid black;
-  background-color: rgba(100, 100, 100, 0.8);
+  background-color: rgba(110, 114, 116, 0.8);
 }
 .highlight {
-  border: 2px solid red;
+  border: 2px solid rgb(235, 52, 52);
   pointer-events: none;
 }
 .label {
diff --git a/tools/winscope/src/SurfaceFlingerTraceView.vue b/tools/winscope/src/SurfaceFlingerTraceView.vue
new file mode 100644
index 0000000..9b9c6ed
--- /dev/null
+++ b/tools/winscope/src/SurfaceFlingerTraceView.vue
@@ -0,0 +1,56 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<template>
+  <TraceView :store="store" :file="file" :summarizer="summarizer" />
+</template>
+
+<script>
+import TraceView from '@/TraceView.vue';
+
+export default {
+  name: 'WindowManagerTraceView',
+  props: ['store', 'file'],
+  components: {
+    TraceView,
+  },
+  methods: {
+    summarizer(layer) {
+      const summary = [];
+
+      if (layer.invisibleDueTo) {
+        summary.push({key: 'Invisible due to', value: layer.invisibleDueTo});
+      }
+
+      if (layer.occludedBy?.length > 0) {
+        summary.push({key: 'Occluded by', value: layer.occludedBy.join(', ')});
+      }
+
+      if (layer.partiallyOccludedBy?.length > 0) {
+        summary.push({
+          key: 'Partially occluded by',
+          value: layer.partiallyOccludedBy.join(', '),
+        });
+      }
+
+      if (layer.coveredBy?.length > 0) {
+        summary.push({key: 'Covered by', value: layer.coveredBy.join(', ')});
+      }
+
+      return summary;
+    },
+  },
+};
+</script>
diff --git a/tools/winscope/src/Timeline.vue b/tools/winscope/src/Timeline.vue
index a1f66ae..293aac6 100644
--- a/tools/winscope/src/Timeline.vue
+++ b/tools/winscope/src/Timeline.vue
@@ -13,50 +13,74 @@
      limitations under the License.
 -->
 <template>
-  <svg width="2000" height="20" viewBox="-5,0,2010,20">
-    <circle :cx="position(item)" cy="10" r="5" v-for="(item, idx) in items" @click="onItemClick(idx)" />
-    <circle v-if="items.length" :cx="position(selected)" cy="10" r="5" class="selected" />
+  <svg
+    width="100%"
+    height="20"
+    class="timeline-svg"
+    :class="{disabled: disabled}"
+    ref="timeline"
+  >
+    <rect
+      :x="`${block.startPos}%`"
+      y="0"
+      :width="`${block.width}%`"
+      :height="pointHeight"
+      :rx="corner"
+      v-for="(block, idx) in timelineBlocks"
+      :key="idx"
+      @click="onBlockClick"
+      class="point"
+    />
+    <rect
+      :x="`${position(selected)}%`"
+      y="0"
+      :width="`${pointWidth}%`"
+      :height="pointHeight"
+      :rx="corner"
+      class="point selected"
+    />
   </svg>
 </template>
 <script>
+import TimelineMixin from "./mixins/Timeline.js";
+
 export default {
-  name: 'timeline',
-  props: ['items', 'selectedIndex', 'scale'],
+  name: "timeline",
+  // TODO: Add indication of trim, at least for collasped timeline
+  props: ["selectedIndex", "crop", "disabled"],
   data() {
-    return {};
+    return {
+      pointHeight: 15,
+      corner: 2
+    };
   },
-  methods: {
-    position(item) {
-      return this.translate(item);
-    },
-    translate(cx) {
-      var scale = [...this.scale];
-      if (scale[0] >= scale[1]) {
-        return cx;
-      }
-      return (cx - scale[0]) / (scale[1] - scale[0]) * 2000;
-    },
-    onItemClick(index) {
-      this.$emit('item-selected', index);
-    },
-  },
+  mixins: [TimelineMixin],
+  methods: {},
   computed: {
     timestamps() {
-      if (this.items.length == 1) {
+      if (this.timeline.length == 1) {
         return [0];
       }
-      return this.items;
+      return this.timeline;
     },
     selected() {
-      return this.items[this.selectedIndex];
-    }
-  },
-}
-
+      return this.timeline[this.selectedIndex];
+    },
+  }
+};
 </script>
 <style scoped>
-.selected {
-  fill: red;
+.timeline-svg .point {
+  cursor: pointer;
 }
-
+.timeline-svg.disabled .point {
+  fill: #BDBDBD;
+  cursor: not-allowed;
+}
+.timeline-svg:not(.disabled) .point.selected {
+  fill: rgb(240, 59, 59);
+}
+.timeline-svg.disabled .point.selected {
+  fill: rgba(240, 59, 59, 0.596);
+}
 </style>
diff --git a/tools/winscope/src/TimelineSelection.vue b/tools/winscope/src/TimelineSelection.vue
new file mode 100644
index 0000000..d4a0e31
--- /dev/null
+++ b/tools/winscope/src/TimelineSelection.vue
@@ -0,0 +1,463 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <div class="wrapper">
+    <svg
+      width="100%"
+      height="20"
+      class="timeline-svg"
+      :class="{disabled: disabled}"
+      ref="timeline"
+    >
+      <rect
+        :x="`${block.startPos}%`"
+        y="0"
+        :width="`${block.width}%`"
+        :height="pointHeight"
+        :rx="corner"
+        v-for="(block, idx) in timelineBlocks"
+        :key="idx"
+        class="point"
+      />
+      <rect
+        v-if="selectedWidth >= 0"
+        v-show="showSelection"
+        :x="selectionAreaStart"
+        y="0"
+        :width="selectedWidth"
+        :height="pointHeight"
+        :rx="corner"
+        class="point selection"
+        ref="selectedSection"
+      />
+      <rect
+        v-else
+        v-show="showSelection"
+        :x="selectionAreaEnd"
+        y="0"
+        :width="-selectedWidth"
+        :height="pointHeight"
+        :rx="corner"
+        class="point selection"
+        ref="selectedSection"
+      />
+
+      <rect
+        v-show="showSelection"
+        :x="selectionAreaStart - 2"
+        y="0"
+        :width="4"
+        :height="pointHeight"
+        :rx="corner"
+        class="point selection-edge"
+        ref="leftResizeDragger"
+      />
+
+      <rect
+        v-show="showSelection"
+        :x="selectionAreaEnd - 2"
+        y="0"
+        :width="4"
+        :height="pointHeight"
+        :rx="corner"
+        class="point selection-edge"
+        ref="rightResizeDragger"
+      />
+    </svg>
+  </div>
+</template>
+<script>
+import TimelineMixin from './mixins/Timeline';
+
+export default {
+  name: 'timelineSelection',
+  props: ['startTimestamp', 'endTimestamp', 'cropArea', 'disabled'],
+  data() {
+    return {
+      pointHeight: 15,
+      corner: 2,
+      selectionStartPosition: 0,
+      selectionEndPosition: 0,
+      selecting: false,
+      dragged: false,
+      draggingSelection: false,
+    };
+  },
+  mixins: [TimelineMixin],
+  watch: {
+    selectionStartPosition() {
+      // Send crop intent rather than final crop value while we are selecting
+      if ((this.selecting && this.dragged)) {
+        this.emitCropIntent();
+        return;
+      }
+
+      this.emitCropDetails();
+    },
+    selectionEndPosition() {
+      // Send crop intent rather than final crop value while we are selecting
+      if ((this.selecting && this.dragged)) {
+        this.emitCropIntent();
+        return;
+      }
+
+      this.emitCropDetails();
+    },
+  },
+  methods: {
+    /**
+     * Create an object that can be injected and removed from the DOM to change
+     * the cursor style. The object is a mask over the entire screen. It is
+     * done this way as opposed to injecting a style targeting all elements for
+     * performance reasons, otherwise recalculate style would be very slow.
+     * This makes sure that regardless of the cursor style of other elements,
+     * the cursor style will be set to what we want over the entire screen.
+     * @param {string} cursor - The cursor type to apply to the entire page.
+     * @return An object that can be injected and removed from the DOM which
+     *         changes the cursor style for the entire page.
+     */
+    createCursorStyle(cursor) {
+      const cursorMask = document.createElement('div');
+      cursorMask.style.cursor = cursor;
+      cursorMask.style.height = '100vh';
+      cursorMask.style.width = '100vw';
+      cursorMask.style.position = 'fixed';
+      cursorMask.style.top = '0';
+      cursorMask.style.left = '0';
+      cursorMask.style['z-index'] = '1000';
+
+      return {
+        inject: () => {
+          document.body.appendChild(cursorMask);
+        },
+        remove: () => {
+          try {
+            document.body.removeChild(cursorMask);
+          } catch (e) {}
+        },
+      };
+    },
+
+    setupCreateSelectionListeners() {
+      const cursorStyle = this.createCursorStyle('crosshair');
+
+      this.timelineSvgMouseDownEventListener = (e) => {
+        e.stopPropagation();
+        this.selecting = true;
+        this.dragged = false;
+        this.mouseDownX = e.offsetX;
+        this.mouseDownClientX = e.clientX;
+
+        cursorStyle.inject();
+      };
+
+      this.createSelectionMouseMoveEventListener = (e) => {
+        if (this.selecting) {
+          if (!this.dragged) {
+            this.selectionStartX = this.mouseDownX;
+          }
+
+          this.dragged = true;
+          const draggedAmount = e.clientX - this.mouseDownClientX;
+
+          if (draggedAmount >= 0) {
+            this.selectionStartPosition = this.selectionStartX;
+
+            const endX = this.selectionStartX + draggedAmount;
+            if (endX <= this.$refs.timeline.clientWidth) {
+              this.selectionEndPosition = endX;
+            } else {
+              this.selectionEndPosition = this.$refs.timeline.clientWidth;
+            }
+
+            this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionEndPosition));
+          } else {
+            this.selectionEndPosition = this.selectionStartX;
+
+            const startX = this.selectionStartX + draggedAmount;
+            if (startX >= 0) {
+              this.selectionStartPosition = startX;
+            } else {
+              this.selectionStartPosition = 0;
+            }
+
+            this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionStartPosition));
+          }
+        }
+      };
+
+      this.createSelectionMouseUpEventListener = (e) => {
+        this.selecting = false;
+        cursorStyle.remove();
+        this.$emit('resetVideoTimestamp');
+        if (this.dragged) {
+          // Clear crop intent, we now have a set crop value
+          this.clearCropIntent();
+          // Notify of final crop value
+          this.emitCropDetails();
+        }
+        this.dragged = false;
+      };
+
+      this.$refs.timeline
+          .addEventListener('mousedown', this.timelineSvgMouseDownEventListener);
+      document
+          .addEventListener('mousemove', this.createSelectionMouseMoveEventListener);
+      document
+          .addEventListener('mouseup', this.createSelectionMouseUpEventListener);
+    },
+
+    teardownCreateSelectionListeners() {
+      this.$refs.timeline
+          .removeEventListener('mousedown', this.timelineSvgMouseDownEventListener);
+      document
+          .removeEventListener('mousemove', this.createSelectionMouseMoveEventListener);
+      document
+          .removeEventListener('mouseup', this.createSelectionMouseUpEventListener);
+    },
+
+    setupDragSelectionListeners() {
+      const cursorStyle = this.createCursorStyle('move');
+
+      this.selectedSectionMouseDownListener = (e) => {
+        e.stopPropagation();
+        this.draggingSelectionStartX = e.clientX;
+        this.selectionStartPosition = this.selectionAreaStart;
+        this.selectionEndPosition = this.selectionAreaEnd;
+        this.draggingSelectionStartPos = this.selectionAreaStart;
+        this.draggingSelectionEndPos = this.selectionAreaEnd;
+
+        // Keep this after fetching selectionAreaStart and selectionAreaEnd.
+        this.draggingSelection = true;
+
+        cursorStyle.inject();
+      };
+
+      this.dragSelectionMouseMoveEventListener = (e) => {
+        if (this.draggingSelection) {
+          const dragAmount = e.clientX - this.draggingSelectionStartX;
+
+          const newStartPos = this.draggingSelectionStartPos + dragAmount;
+          const newEndPos = this.draggingSelectionEndPos + dragAmount;
+          if (newStartPos >= 0 && newEndPos <= this.$refs.timeline.clientWidth) {
+            this.selectionStartPosition = newStartPos;
+            this.selectionEndPosition = newEndPos;
+          } else {
+            if (newStartPos < 0) {
+              this.selectionStartPosition = 0;
+              this.selectionEndPosition = newEndPos - (newStartPos /* negative overflown amount*/);
+            } else {
+              const overflownAmount = newEndPos - this.$refs.timeline.clientWidth;
+              this.selectionEndPosition = this.$refs.timeline.clientWidth;
+              this.selectionStartPosition = newStartPos - overflownAmount;
+            }
+          }
+        }
+      };
+
+      this.dragSelectionMouseUpEventListener = (e) => {
+        this.draggingSelection = false;
+        cursorStyle.remove();
+      };
+
+      this.$refs.selectedSection
+          .addEventListener('mousedown', this.selectedSectionMouseDownListener);
+      document
+          .addEventListener('mousemove', this.dragSelectionMouseMoveEventListener);
+      document
+          .addEventListener('mouseup', this.dragSelectionMouseUpEventListener);
+    },
+
+    teardownDragSelectionListeners() {
+      this.$refs.selectedSection
+          .removeEventListener('mousedown', this.selectedSectionMouseDownListener);
+      document
+          .removeEventListener('mousemove', this.dragSelectionMouseMoveEventListener);
+      document
+          .removeEventListener('mouseup', this.dragSelectionMouseUpEventListener);
+    },
+
+    setupResizeSelectionListeners() {
+      const cursorStyle = this.createCursorStyle('ew-resize');
+
+      this.leftResizeDraggerMouseDownEventListener = (e) => {
+        e.stopPropagation();
+        this.resizeStartX = e.clientX;
+        this.selectionStartPosition = this.selectionAreaStart;
+        this.selectionEndPosition = this.selectionAreaEnd;
+        this.resizeStartPos = this.selectionAreaStart;
+        this.resizeingLeft = true;
+
+        cursorStyle.inject();
+        this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionAreaStart));
+      };
+
+      this.rightResizeDraggerMouseDownEventListener = (e) => {
+        e.stopPropagation();
+        this.resizeStartX = e.clientX;
+        this.selectionStartPosition = this.selectionAreaStart;
+        this.selectionEndPosition = this.selectionAreaEnd;
+        this.resizeEndPos = this.selectionAreaEnd;
+        this.resizeingRight = true;
+
+        cursorStyle.inject();
+        this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionAreaEnd));
+      };
+
+      this.resizeMouseMoveEventListener = (e) => {
+        if (this.resizeingLeft) {
+          const moveAmount = e.clientX - this.resizeStartX;
+          let newStartPos = this.resizeStartPos + moveAmount;
+          if (newStartPos >= this.selectionEndPosition) {
+            newStartPos = this.selectionEndPosition;
+          }
+          if (newStartPos < 0) {
+            newStartPos = 0;
+          }
+
+          this.selectionStartPosition = newStartPos;
+
+          this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionStartPosition));
+        }
+
+        if (this.resizeingRight) {
+          const moveAmount = e.clientX - this.resizeStartX;
+          let newEndPos = this.resizeEndPos + moveAmount;
+          if (newEndPos <= this.selectionStartPosition) {
+            newEndPos = this.selectionStartPosition;
+          }
+          if (newEndPos > this.$refs.timeline.clientWidth) {
+            newEndPos = this.$refs.timeline.clientWidth;
+          }
+
+          this.selectionEndPosition = newEndPos;
+          this.$emit('showVideoAt', this.absolutePositionAsTimestamp(this.selectionEndPosition));
+        }
+      };
+
+      this.resizeSelectionMouseUpEventListener = (e) => {
+        this.resizeingLeft = false;
+        this.resizeingRight = false;
+        cursorStyle.remove();
+        this.$emit('resetVideoTimestamp');
+      };
+
+      this.$refs.leftResizeDragger
+          .addEventListener('mousedown', this.leftResizeDraggerMouseDownEventListener);
+      this.$refs.rightResizeDragger
+          .addEventListener('mousedown', this.rightResizeDraggerMouseDownEventListener);
+      document
+          .addEventListener('mousemove', this.resizeMouseMoveEventListener);
+      document
+          .addEventListener('mouseup', this.resizeSelectionMouseUpEventListener);
+    },
+
+    teardownResizeSelectionListeners() {
+      this.$refs.leftResizeDragger
+          .removeEventListener('mousedown', this.leftResizeDraggerMouseDownEventListener);
+      this.$refs.rightResizeDragger
+          .removeEventListener('mousedown', this.rightResizeDraggerMouseDownEventListener);
+      document
+          .removeEventListener('mousemove', this.resizeMouseMoveEventListener);
+      document
+          .removeEventListener('mouseup', this.resizeSelectionMouseUpEventListener);
+    },
+
+    emitCropDetails() {
+      const width = this.$refs.timeline.clientWidth;
+      this.$emit('crop', {
+        left: this.selectionStartPosition / width,
+        right: this.selectionEndPosition / width,
+      });
+    },
+
+    emitCropIntent() {
+      const width = this.$refs.timeline.clientWidth;
+      this.$emit('cropIntent', {
+        left: this.selectionStartPosition / width,
+        right: this.selectionEndPosition / width
+      });
+    },
+
+    clearCropIntent() {
+      this.$emit('cropIntent', null);
+    }
+  },
+  computed: {
+    selected() {
+      return this.timeline[this.selectedIndex];
+    },
+    selectedWidth() {
+      return this.selectionAreaEnd - this.selectionAreaStart;
+    },
+    showSelection() {
+      return this.selectionAreaStart || this.selectionAreaEnd;
+    },
+    selectionAreaStart() {
+      if ((this.selecting && this.dragged) || this.draggingSelection) {
+        return this.selectionStartPosition;
+      }
+
+      if (this.cropArea && this.$refs.timeline) {
+        return this.cropArea.left * this.$refs.timeline.clientWidth;
+      }
+
+      return 0;
+    },
+    selectionAreaEnd() {
+      if ((this.selecting && this.dragged) || this.draggingSelection) {
+        return this.selectionEndPosition;
+      }
+
+      if (this.cropArea && this.$refs.timeline) {
+        return this.cropArea.right * this.$refs.timeline.clientWidth;
+      }
+
+      return 0;
+    },
+  },
+  mounted() {
+    this.setupCreateSelectionListeners();
+    this.setupDragSelectionListeners();
+    this.setupResizeSelectionListeners();
+  },
+  beforeDestroy() {
+    this.teardownCreateSelectionListeners();
+    this.teardownDragSelectionListeners();
+    this.teardownResizeSelectionListeners();
+  },
+};
+</script>
+<style scoped>
+.wrapper {
+  padding: 0 15px;
+}
+
+.timeline-svg {
+  cursor: crosshair;
+}
+.timeline-svg .point {
+  fill: #BDBDBD;
+}
+.timeline-svg .point.selection {
+  fill: rgba(240, 59, 59, 0.596);
+  cursor: move;
+}
+
+.timeline-svg .point.selection-edge {
+  fill: rgba(27, 123, 212, 0.596);
+  cursor: ew-resize;
+}
+</style>
diff --git a/tools/winscope/src/Timelines.vue b/tools/winscope/src/Timelines.vue
new file mode 100644
index 0000000..a322996
--- /dev/null
+++ b/tools/winscope/src/Timelines.vue
@@ -0,0 +1,376 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<template>
+  <div class="timelines-container">
+
+    <div class="timeline-icons" @mousedown="mousedownHandler">
+      <div
+        v-for="file in timelineFiles"
+        :key="file.filename"
+        class="trace-icon"
+        :class="{disabled: file.timelineDisabled}"
+        @click="toggleTimeline(file)"
+        style="cursor: pointer;"
+      >
+        <i class="material-icons">
+          {{ TRACE_ICONS[file.type] }}
+          <md-tooltip md-direction="bottom">{{ file.type }}</md-tooltip>
+        </i>
+      </div>
+    </div>
+
+    <div class="timelines-wrapper" ref="timelinesWrapper">
+      <md-list class="timelines" @mousedown="mousedownHandler" ref="timelines">
+        <md-list-item
+        v-for="file in timelineFiles"
+        :key="file.filename"
+        >
+          <timeline
+            :timeline="Object.freeze(file.timeline)"
+            :selected-index="file.selectedIndex"
+            :scale="scale"
+            :crop="crop"
+            :disabled="file.timelineDisabled"
+            class="timeline"
+          />
+        </md-list-item>
+      </md-list>
+
+      <div
+        class="selection"
+        :style="selectionStyle"
+      />
+
+      <div
+        v-show="this.cropIntent"
+        class="selection-intent"
+        :style="selectionIntentStyle"
+      />
+    </div>
+  </div>
+</template>
+<script>
+import Timeline from './Timeline.vue';
+import {TRACE_ICONS} from '@/decode.js';
+
+export default {
+  name: 'Timelines',
+  props: ['timelineFiles', 'scale', 'crop', 'cropIntent'],
+  data() {
+    return {
+      // Distances of sides from top left corner of wrapping div in pixels
+      selectionPosition: {
+        top: 0,
+        left: 0,
+        bottom: 0,
+        right: 0,
+      },
+      TRACE_ICONS,
+    };
+  },
+  computed: {
+    /**
+     * Used to check whether or not a selection box should be displayed.
+     * @return {bool} true if any of the positions are non nullish values
+     */
+    isEmptySelection() {
+      return this.selectionPosition.top ||
+        this.selectionPosition.left ||
+        this.selectionPosition.bottom ||
+        this.selectionPosition.right;
+    },
+    /**
+     * Generates the style of the selection box.
+     * @return {object} an object containing the style of the selection box.
+     */
+    selectionStyle() {
+      return {
+        top: `${this.selectionPosition.top}px`,
+        left: `${this.selectionPosition.left}px`,
+        height:
+          `${this.selectionPosition.bottom - this.selectionPosition.top}px`,
+        width:
+          `${this.selectionPosition.right - this.selectionPosition.left}px`,
+      };
+    },
+    /**
+     * Generates the dynamic style of the selection intent box.
+     * @return {object} an object containing the style of the selection intent
+     *                  box.
+     */
+    selectionIntentStyle() {
+      if (!(this.cropIntent && this.$refs.timelinesWrapper)) {
+        return {
+          left: 0,
+          width: 0,
+        };
+      }
+
+      const activeCropLeft = this.crop?.left ?? 0;
+      const activeCropRight = this.crop?.right ?? 1;
+      const timelineWidth =
+        this.$refs.timelinesWrapper.getBoundingClientRect().width;
+
+      const r = timelineWidth / (activeCropRight - activeCropLeft);
+
+      let left = 0;
+      let boderLeft = 'none';
+      if (this.cropIntent.left > activeCropLeft) {
+        left = (this.cropIntent.left - activeCropLeft) * r;
+        boderLeft = null;
+      }
+
+      let right = timelineWidth;
+      let borderRight = 'none';
+      if (this.cropIntent.right < activeCropRight) {
+        right = timelineWidth - (activeCropRight - this.cropIntent.right) * r;
+        borderRight = null;
+      }
+
+      return {
+        'left': `${left}px`,
+        'width': `${right - left}px`,
+        'border-left': boderLeft,
+        'border-right': borderRight,
+      };
+    },
+  },
+  methods: {
+    /**
+     * Adds an overlay to make sure element selection can't happen and the
+     * crosshair cursor style is maintained wherever the curso is on the screen
+     * while a selection is taking place.
+     */
+    addOverlay() {
+      if (this.overlay) {
+        return;
+      }
+
+      this.overlay = document.createElement('div');
+      Object.assign(this.overlay.style, {
+        'position': 'fixed',
+        'top': 0,
+        'left': 0,
+        'height': '100vh',
+        'width': '100vw',
+        'z-index': 100,
+        'cursor': 'crosshair',
+      });
+
+      document.body.appendChild(this.overlay);
+    },
+
+    /**
+     * Removes the overlay that is added by a call to addOverlay.
+     */
+    removeOverlay() {
+      if (!this.overlay) {
+        return;
+      }
+
+      document.body.removeChild(this.overlay);
+      delete this.overlay;
+    },
+
+    /**
+     * Generates an object that can is used to update the position and style of
+     * the selection box when a selection is being made. The object contains
+     * three functions which all take a DOM event as a parameter.
+     *
+     * - init: setup the initial drag position of the selection base on the
+     *         mousedown event
+     * - update: updates the selection box's coordinates based on the mousemouve
+     *           event
+     * - reset: clears the selection box, shold be called when the mouseup event
+     *          occurs or when we want to no longer display the selection box.
+     * @return {null}
+     */
+    selectionPositionsUpdater() {
+      let startClientX; let startClientY; let x; let y;
+
+      return {
+        init: (e) => {
+          startClientX = e.clientX;
+          startClientY = e.clientY;
+          x = startClientX -
+            this.$refs.timelines.$el.getBoundingClientRect().left;
+          y = startClientY -
+            this.$refs.timelines.$el.getBoundingClientRect().top;
+        },
+        update: (e) => {
+          let left; let right; let top; let bottom;
+
+          const xDiff = e.clientX - startClientX;
+          if (xDiff > 0) {
+            left = x;
+            right = x + xDiff;
+          } else {
+            left = x + xDiff;
+            right = x;
+          }
+
+          const yDiff = e.clientY - startClientY;
+          if (yDiff > 0) {
+            top = y;
+            bottom = y + yDiff;
+          } else {
+            top = y + yDiff;
+            bottom = y;
+          }
+
+          if (left < 0) {
+            left = 0;
+          }
+          if (top < 0) {
+            top = 0;
+          }
+          if (right > this.$refs.timelines.$el.getBoundingClientRect().width) {
+            right = this.$refs.timelines.$el.getBoundingClientRect().width;
+          }
+
+          if (bottom >
+            this.$refs.timelines.$el.getBoundingClientRect().height) {
+            bottom = this.$refs.timelines.$el.getBoundingClientRect().height;
+          }
+
+          this.$set(this.selectionPosition, 'left', left);
+          this.$set(this.selectionPosition, 'right', right);
+          this.$set(this.selectionPosition, 'top', top);
+          this.$set(this.selectionPosition, 'bottom', bottom);
+        },
+        reset: (e) => {
+          this.$set(this.selectionPosition, 'left', 0);
+          this.$set(this.selectionPosition, 'right', 0);
+          this.$set(this.selectionPosition, 'top', 0);
+          this.$set(this.selectionPosition, 'bottom', 0);
+        },
+      };
+    },
+
+    /**
+     * Handles the mousedown event indicating the start of a selection.
+     * Adds listeners to handles mousemove and mouseup event to detect the
+     * selection and update the selection box's coordinates.
+     * @param {event} e
+     */
+    mousedownHandler(e) {
+      const selectionPositionsUpdater = this.selectionPositionsUpdater();
+      selectionPositionsUpdater.init(e);
+
+      let dragged = false;
+
+      const mousemoveHandler = (e) => {
+        if (!dragged) {
+          dragged = true;
+          this.addOverlay();
+        }
+
+        selectionPositionsUpdater.update(e);
+      };
+      document.addEventListener('mousemove', mousemoveHandler);
+
+      const mouseupHandler = (e) => {
+        document.removeEventListener('mousemove', mousemoveHandler);
+        document.removeEventListener('mouseup', mouseupHandler);
+
+        if (dragged) {
+          this.removeOverlay();
+          selectionPositionsUpdater.update(e);
+          this.zoomToSelection();
+        }
+        selectionPositionsUpdater.reset();
+      };
+      document.addEventListener('mouseup', mouseupHandler);
+    },
+
+    /**
+     * Update the crop values to zoom into the timeline based on the currently
+     * set selection box coordinates.
+     */
+    zoomToSelection() {
+      const left = this.crop?.left ?? 0;
+      const right = this.crop?.right ?? 1;
+
+      const ratio =
+        (this.selectionPosition.right - this.selectionPosition.left) /
+        this.$refs.timelines.$el.getBoundingClientRect().width;
+
+      const newCropWidth = ratio * (right - left);
+      const newLeft = left + (this.selectionPosition.left /
+        this.$refs.timelines.$el.getBoundingClientRect().width) *
+          (right - left);
+
+      if (this.crop) {
+        this.$set(this.crop, 'left', newLeft);
+        this.$set(this.crop, 'right', newLeft + newCropWidth);
+      } else {
+        this.$emit('crop', {
+          left: newLeft,
+          right: newLeft + newCropWidth,
+        });
+      }
+    },
+
+    toggleTimeline(file) {
+      this.$set(file, 'timelineDisabled', !file.timelineDisabled);
+    },
+  },
+  components: {
+    Timeline,
+  },
+};
+</script>
+<style scoped>
+.timelines-container {
+  display: flex;
+}
+
+.timelines-container .timelines-wrapper {
+  flex-grow: 1;
+  cursor: crosshair;
+  position: relative;
+}
+
+.timelines-wrapper {
+  overflow: hidden;
+}
+
+.selection, .selection-intent {
+  position: absolute;
+  z-index: 100;
+  background: rgba(255, 36, 36, 0.5);
+  pointer-events: none;
+}
+
+.selection-intent {
+  top: 0;
+  height: 100%;
+  margin-left: -3px;
+  border-left: 3px #1261A0 solid;
+  border-right: 3px #1261A0 solid;
+}
+
+.timeline-icons {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-evenly;
+  margin-left: 15px;
+}
+
+.trace-icon.disabled {
+  color: gray;
+}
+</style>
diff --git a/tools/winscope/src/TraceView.vue b/tools/winscope/src/TraceView.vue
index 0cdd581..52016b7 100644
--- a/tools/winscope/src/TraceView.vue
+++ b/tools/winscope/src/TraceView.vue
@@ -14,55 +14,129 @@
 -->
 <template>
   <md-card-content class="container">
-    <md-card class="rects" v-if="hasScreenView">
-      <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
-        <h2 class="md-title">Screen</h2>
-      </md-whiteframe>
-      <md-whiteframe md-elevation="8">
-        <rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" />
-      </md-whiteframe>
-    </md-card>
-    <md-card class="hierarchy">
-      <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
-        <h2 class="md-title" style="flex: 1;">Hierarchy</h2>
-        <md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
-        <md-checkbox v-model="store.flattened">Flat</md-checkbox>
-        <input id="filter" type="search" placeholder="Filter..." v-model="hierarchyPropertyFilterString" />
-      </md-whiteframe>
-      <tree-view class="data-card" :item="tree" @item-selected="itemSelected" :selected="hierarchySelected" :filter="hierarchyFilter" :flattened="store.flattened" ref="hierarchy" />
-    </md-card>
-    <md-card class="properties">
-      <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
-        <h2 class="md-title" style="flex: 1">Properties</h2>
-        <div class="filter">
-          <input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" />
+    <div class="rects" v-if="hasScreenView">
+      <rects
+        :bounds="bounds"
+        :rects="rects"
+        :highlight="highlight"
+        @rect-click="onRectClick"
+      />
+    </div>
+
+    <div class="hierarchy">
+      <flat-card>
+        <md-content
+          md-tag="md-toolbar"
+          md-elevation="0"
+          class="card-toolbar md-transparent md-dense"
+        >
+          <h2 class="md-title" style="flex: 1;">Hierarchy</h2>
+          <md-checkbox
+            v-model="showHierachyDiff"
+            v-if="diffVisualizationAvailable"
+          >
+            Show Diff
+          </md-checkbox>
+          <md-checkbox v-model="store.simplifyNames">
+            Simplify names
+          </md-checkbox>
+          <md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
+          <md-checkbox v-model="store.flattened">Flat</md-checkbox>
+          <md-field md-inline class="filter">
+            <label>Filter...</label>
+            <md-input v-model="hierarchyPropertyFilterString"></md-input>
+          </md-field>
+        </md-content>
+        <div class="tree-view-wrapper">
+          <tree-view
+            class="treeview"
+            :item="tree"
+            @item-selected="itemSelected"
+            :selected="hierarchySelected"
+            :filter="hierarchyFilter"
+            :flattened="store.flattened"
+            :items-clickable="true"
+            :useGlobalCollapsedState="true"
+            :simplify-names="store.simplifyNames"
+            ref="hierarchy"
+          />
         </div>
-      </md-whiteframe>
-      <tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" />
-    </md-card>
+      </flat-card>
+    </div>
+
+    <div class="properties">
+      <flat-card>
+        <md-content
+          md-tag="md-toolbar"
+          md-elevation="0"
+          class="card-toolbar md-transparent md-dense"
+        >
+          <h2 class="md-title" style="flex: 1">Properties</h2>
+          <md-checkbox
+            v-model="showPropertiesDiff"
+            v-if="diffVisualizationAvailable"
+          >
+            Show Diff
+          </md-checkbox>
+          <md-field md-inline class="filter">
+            <label>Filter...</label>
+            <md-input v-model="propertyFilterString"></md-input>
+          </md-field>
+        </md-content>
+        <div class="properties-content">
+          <div v-if="elementSummary" class="element-summary">
+            <div v-for="elem in elementSummary" v-bind:key="elem.key">
+              <!-- eslint-disable-next-line max-len -->
+              <span class="key">{{ elem.key }}:</span> <span class="value">{{ elem.value }}</span>
+            </div>
+          </div>
+          <div v-if="selectedTree" class="tree-view-wrapper">
+            <tree-view
+              class="treeview"
+              :item="selectedTree"
+              :filter="propertyFilter"
+              :collapseChildren="true"
+              :useGlobalCollapsedState="true"
+              :elementView="PropertiesTreeElement"
+            />
+          </div>
+          <div class="no-properties" v-else>
+            <i class="material-icons none-icon">
+              filter_none
+            </i>
+            <span>No element selected in the hierachy.</span>
+          </div>
+        </div>
+      </flat-card>
+    </div>
+
   </md-card-content>
 </template>
 <script>
-import TreeView from './TreeView.vue'
-import Timeline from './Timeline.vue'
-import Rects from './Rects.vue'
+import TreeView from './TreeView.vue';
+import Rects from './Rects.vue';
+import FlatCard from './components/FlatCard.vue';
+import PropertiesTreeElement from './PropertiesTreeElement.vue';
 
-import { transform_json } from './transform.js'
-import { format_transform_type, is_simple_transform } from './matrix_utils.js'
-import { DATA_TYPES } from './decode.js'
+import {ObjectTransformer} from './transform.js';
+import {DiffGenerator, defaultModifiedCheck} from './utils/diff.js';
+// eslint-disable-next-line camelcase
+import {format_transform_type, is_simple_transform} from './matrix_utils.js';
+import {TRACE_TYPES, DUMP_TYPES} from './decode.js';
+import {stableIdCompatibilityFixup} from './utils/utils.js';
+import {CompatibleFeatures} from './utils/compatibility.js';
 
 function formatColorTransform(vals) {
-    const fixedVals = vals.map(v => v.toFixed(1));
-    var formatted = ``;
-    for (var i = 0; i < fixedVals.length; i += 4) {
-      formatted += `[`;
-      formatted += fixedVals.slice(i, i + 4).join(", ");
-      formatted += `] `;
-    }
-    return formatted;
+  const fixedVals = vals.map((v) => v.toFixed(1));
+  let formatted = ``;
+  for (let i = 0; i < fixedVals.length; i += 4) {
+    formatted += `[`;
+    formatted += fixedVals.slice(i, i + 4).join(', ');
+    formatted += `] `;
+  }
+  return formatted;
 }
 
-
 function formatProto(obj) {
   if (!obj || !obj.$type) {
     return;
@@ -70,7 +144,8 @@
   if (obj.$type.name === 'RectProto') {
     return `(${obj.left}, ${obj.top})  -  (${obj.right}, ${obj.bottom})`;
   } else if (obj.$type.name === 'FloatRectProto') {
-    return `(${obj.left.toFixed(3)}, ${obj.top.toFixed(3)})  -  (${obj.right.toFixed(3)}, ${obj.bottom.toFixed(3)})`;
+    return `(${obj.left.toFixed(3)}, ${obj.top.toFixed(3)})  -  ` +
+        `(${obj.right.toFixed(3)}, ${obj.bottom.toFixed(3)})`;
   } else if (obj.$type.name === 'PositionProto') {
     return `(${obj.x.toFixed(3)}, ${obj.y.toFixed(3)})`;
   } else if (obj.$type.name === 'SizeProto') {
@@ -78,64 +153,119 @@
   } else if (obj.$type.name === 'ColorProto') {
     return `r:${obj.r} g:${obj.g} \n b:${obj.b} a:${obj.a}`;
   } else if (obj.$type.name === 'TransformProto') {
-    var transform_type = format_transform_type(obj);
+    const transformType = format_transform_type(obj);
     if (is_simple_transform(obj)) {
-      return `${transform_type}`;
+      return `${transformType}`;
     }
-    return `${transform_type}  dsdx:${obj.dsdx.toFixed(3)}   dtdx:${obj.dtdx.toFixed(3)}   dsdy:${obj.dsdy.toFixed(3)}   dtdy:${obj.dtdy.toFixed(3)}`;
+    return `${transformType}  dsdx:${obj.dsdx.toFixed(3)}   ` +
+        `dtdx:${obj.dtdx.toFixed(3)}   dsdy:${obj.dsdy.toFixed(3)}   ` +
+        `dtdy:${obj.dtdy.toFixed(3)}`;
   } else if (obj.$type.name === 'ColorTransformProto') {
-    var formated = formatColorTransform(obj.val);
+    const formated = formatColorTransform(obj.val);
     return `${formated}`;
   }
 }
 
+function findEntryInTree(tree, id) {
+  if (tree.stableId === id) {
+    return tree;
+  }
+
+  if (!tree.children) {
+    return null;
+  }
+
+  for (const child of tree.children) {
+    const foundEntry = findEntryInTree(child, id);
+    if (foundEntry) {
+      return foundEntry;
+    }
+  }
+
+  return null;
+}
+
 export default {
   name: 'traceview',
+  props: ['store', 'file', 'summarizer'],
   data() {
     return {
-      propertyFilterString: "",
-      hierarchyPropertyFilterString:"",
-      selectedTree: {},
+      propertyFilterString: '',
+      hierarchyPropertyFilterString: '',
+      selectedTree: null,
       hierarchySelected: null,
       lastSelectedStableId: null,
       bounds: {},
       rects: [],
+      item: null,
       tree: null,
       highlight: null,
-    }
+      showHierachyDiff: false,
+      showPropertiesDiff: false,
+      PropertiesTreeElement,
+    };
   },
   methods: {
     itemSelected(item) {
       this.hierarchySelected = item;
-      this.selectedTree = transform_json(item.obj, item.name, {
-        skip: item.skip,
-        formatter: formatProto
-      });
-      this.highlight = item.highlight;
+      this.selectedTree = this.getTransformedProperties(item);
+      this.highlight = item.rect;
       this.lastSelectedStableId = item.stableId;
       this.$emit('focus');
     },
+    getTransformedProperties(item) {
+      const transformer = new ObjectTransformer(
+          item.obj,
+          item.name,
+          stableIdCompatibilityFixup(item),
+      ).setOptions({
+        skip: item.skip,
+        formatter: formatProto,
+      });
+
+      if (this.showPropertiesDiff && this.diffVisualizationAvailable) {
+        const prevItem = this.getItemFromPrevTree(item);
+        transformer.withDiff(prevItem?.obj);
+      }
+
+      return transformer.transform();
+    },
     onRectClick(item) {
       if (item) {
         this.itemSelected(item);
       }
     },
+    generateTreeFromItem(item) {
+      if (!this.showHierachyDiff || !this.diffVisualizationAvailable) {
+        return item;
+      }
+
+      return new DiffGenerator(this.item)
+          .compareWith(this.getDataWithOffset(-1))
+          .withUniqueNodeId((node) => {
+            return node.stableId;
+          })
+          .withModifiedCheck(defaultModifiedCheck)
+          .generateDiffTree();
+    },
     setData(item) {
-      this.tree = item;
+      this.item = item;
+      this.tree = this.generateTreeFromItem(item);
+
       this.rects = [...item.rects].reverse();
       this.bounds = item.bounds;
 
       this.hierarchySelected = null;
-      this.selectedTree = {};
+      this.selectedTree = null;
       this.highlight = null;
 
-      function find_item(item, stableId) {
+      function findItem(item, stableId) {
         if (item.stableId === stableId) {
           return item;
         }
         if (Array.isArray(item.children)) {
-          for (var child of item.children) {
-            var found = find_item(child, stableId);
+          for (const child of item.children) {
+            const found = findItem(child, stableId);
             if (found) {
               return found;
             }
@@ -145,7 +275,7 @@
       }
 
       if (this.lastSelectedStableId) {
-        var found = find_item(item, this.lastSelectedStableId);
+        const found = findItem(item, this.lastSelectedStableId);
         if (found) {
           this.itemSelected(found);
         }
@@ -157,53 +287,119 @@
     arrowDown() {
       return this.$refs.hierarchy.selectNext();
     },
+    getDataWithOffset(offset) {
+      const index = this.file.selectedIndex + offset;
+
+      if (index < 0 || index >= this.file.data.length) {
+        return null;
+      }
+
+      return this.file.data[index];
+    },
+    getItemFromPrevTree(entry) {
+      if (!this.showPropertiesDiff || !this.hierarchySelected) {
+        return null;
+      }
+
+      const id = entry.stableId;
+      if (!id) {
+        throw new Error('Entry has no stableId...');
+      }
+
+      const prevTree = this.getDataWithOffset(-1);
+      if (!prevTree) {
+        console.warn('No previous entry');
+        return null;
+      }
+
+      const prevEntry = findEntryInTree(prevTree, id);
+      if (!prevEntry) {
+        console.warn('Didn\'t exist in last entry');
+        // TODO: Maybe handle this in some way.
+      }
+
+      return prevEntry;
+    },
   },
   created() {
-    this.setData(this.file.data[this.file.selectedIndex]);
+    this.setData(this.file.data[this.file.selectedIndex ?? 0]);
   },
   watch: {
     selectedIndex() {
-      this.setData(this.file.data[this.file.selectedIndex]);
-    }
+      this.setData(this.file.data[this.file.selectedIndex ?? 0]);
+    },
+    showHierachyDiff() {
+      this.tree = this.generateTreeFromItem(this.item);
+    },
+    showPropertiesDiff() {
+      if (this.hierarchySelected) {
+        this.selectedTree =
+            this.getTransformedProperties(this.hierarchySelected);
+      }
+    },
   },
-  props: ['store', 'file'],
   computed: {
+    diffVisualizationAvailable() {
+      return CompatibleFeatures.DiffVisualization && (
+        this.file.type == TRACE_TYPES.WINDOW_MANAGER ||
+          this.file.type == TRACE_TYPES.SURFACE_FLINGER
+      );
+    },
     selectedIndex() {
       return this.file.selectedIndex;
     },
     hierarchyFilter() {
-      var hierarchyPropertyFilter = getFilter(this.hierarchyPropertyFilterString);
+      const hierarchyPropertyFilter =
+          getFilter(this.hierarchyPropertyFilterString);
       return this.store.onlyVisible ? (c) => {
-        return c.visible && hierarchyPropertyFilter(c);} : hierarchyPropertyFilter;
+        return c.visible && hierarchyPropertyFilter(c);
+      } : hierarchyPropertyFilter;
     },
     propertyFilter() {
       return getFilter(this.propertyFilterString);
     },
     hasScreenView() {
-      return this.file.type !== DATA_TYPES.TRANSACTION;
+      return this.file.type == TRACE_TYPES.WINDOW_MANAGER ||
+          this.file.type == TRACE_TYPES.SURFACE_FLINGER ||
+          this.file.type == DUMP_TYPES.WINDOW_MANAGER ||
+          this.file.type == DUMP_TYPES.SURFACE_FLINGER;
+    },
+    elementSummary() {
+      if (!this.hierarchySelected || !this.summarizer) {
+        return null;
+      }
+
+      const summary = this.summarizer(this.hierarchySelected);
+
+      if (summary?.length === 0) {
+        return null;
+      }
+
+      return summary;
     },
   },
   components: {
     'tree-view': TreeView,
     'rects': Rects,
-  }
-}
+    'flat-card': FlatCard,
+  },
+};
 
 function getFilter(filterString) {
-  var filterStrings = filterString.split(",");
-  var positive = [];
-  var negative = [];
+  const filterStrings = filterString.split(',');
+  const positive = [];
+  const negative = [];
   filterStrings.forEach((f) => {
-    if (f.startsWith("!")) {
-      var str = f.substring(1);
+    if (f.startsWith('!')) {
+      const str = f.substring(1);
       negative.push((s) => s.indexOf(str) === -1);
     } else {
-      var str = f;
+      const str = f;
       positive.push((s) => s.indexOf(str) !== -1);
     }
   });
-  var filter = (item) => {
-    var apply = (f) => f(String(item.name));
+  const filter = (item) => {
+    const apply = (f) => f(String(item.name));
     return (positive.length === 0 || positive.some(apply)) &&
           (negative.length === 0 || negative.every(apply));
   };
@@ -211,7 +407,12 @@
 }
 
 </script>
-<style>
+<style scoped>
+.container {
+  display: flex;
+  flex-wrap: wrap;
+}
+
 .rects {
   flex: none;
   margin: 8px;
@@ -222,6 +423,19 @@
   flex: 1;
   margin: 8px;
   min-width: 400px;
+  min-height: 50rem;
+}
+
+.rects,
+.hierarchy,
+.properties {
+  padding: 5px;
+}
+
+.flat-card {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
 }
 
 .hierarchy>.tree-view,
@@ -229,15 +443,60 @@
   margin: 16px;
 }
 
-.data-card {
+.treeview {
   overflow: auto;
-  max-height: 48em;
-}
-
-.pre-line-data-card {
-  overflow: auto;
-  max-height: 48em;
   white-space: pre-line;
 }
 
+.no-properties {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  align-self: center;
+  align-items: center;
+  justify-content: center;
+  padding: 50px 25px;
+}
+
+.no-properties .none-icon {
+  font-size: 35px;
+  margin-bottom: 10px;
+}
+
+.no-properties span {
+  font-weight: 100;
+}
+
+.filter {
+  width: auto;
+}
+
+.element-summary {
+  padding: 1rem;
+  border-bottom: thin solid rgba(0,0,0,.12);
+}
+
+.element-summary .key {
+  font-weight: 500;
+}
+
+.element-summary .value {
+  color: rgba(0, 0, 0, 0.75);
+}
+
+.properties-content {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+}
+
+.tree-view-wrapper {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+}
+
+.treeview {
+  flex: 1 0 0;
+}
 </style>
diff --git a/tools/winscope/src/TransactionEntry.vue b/tools/winscope/src/TransactionEntry.vue
new file mode 100644
index 0000000..24a80e0
--- /dev/null
+++ b/tools/winscope/src/TransactionEntry.vue
@@ -0,0 +1,271 @@
+<template>
+  <div>
+
+    <div v-if="source.type === 'vsyncEvent'" class="vsync">
+      <div class="vsync-dot" />
+      <md-tooltip md-direction="left">
+        VSync
+      </md-tooltip>
+    </div>
+
+    <div v-else
+      class="entry"
+      :class="{
+        inactive: source.timestamp > currentTimestamp,
+        selected: isSelected
+      }"
+      @click="onClick(source)"
+    >
+      <div class="time-column">
+        <a @click="e => setTimelineTime(e, source.timestamp)" class="time-link">
+          {{source.time}}
+        </a>
+        <div
+          class="new-badge"
+          :style="{visibility: source.new ? 'visible' : 'hidden'} "
+        >
+          New
+        </div>
+      </div>
+      <div class="type-column">{{transactionTypeOf(source)}}</div>
+      <div class="affected-surfaces-column">
+        <span
+          v-for="(surface, index) in sufacesAffectedBy(source)"
+          v-bind:key="surface.id"
+        >
+          <!-- eslint-disable-next-line max-len -->
+          <span v-if="surface.name" class="surface-name">{{ surface.name }}</span>
+          <span class="surface-id">
+            <!-- eslint-disable-next-line max-len -->
+            <span v-if="surface.name">(</span>{{surface.id}}<span v-if="surface.name">)</span>
+          </span>
+          <!-- eslint-disable-next-line max-len -->
+          <span v-if="index + 1 < sufacesAffectedBy(source).length">,&nbsp;</span>
+        </span>
+      </div>
+      <div class="extra-info-column">
+        <span v-if="source.identifier">
+          <!-- eslint-disable-next-line max-len -->
+          Tx Id: <span class="light">{{ prettifyTransactionId(source.identifier) }}</span><br/>
+        </span>
+        <span v-if="source.origin">
+          PID: <span class="light">{{ source.origin.pid }}</span><br/>
+          UID: <span class="light">{{ source.origin.uid }}</span><br/>
+        </span>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'transaction-entry',
+  props: {
+    index: {
+      type: Number,
+    },
+    source: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+    onClick: {
+      type: Function,
+    },
+    selectedTransaction: {
+      type: Object,
+    },
+    transactionsTrace: {
+      type: Object,
+    },
+    prettifyTransactionId: {
+      type: Function,
+    },
+  },
+  computed: {
+    currentTimestamp() {
+      return this.$store.state.currentTimestamp;
+    },
+    isSelected() {
+      return this.source === this.selectedTransaction;
+    },
+    hasOverrideChangeDueToMerge() {
+      const transaction = this.source;
+
+      if (!transaction.identifier) {
+        return;
+      }
+
+      // console.log('transaction', transaction.identifier);
+
+      // const history = this.transactionsTrace.transactionHistory;
+
+      // const allTransactionsMergedInto = history
+      //     .allTransactionsMergedInto(transaction.identifier);
+      // console.log('All merges', allTransactionsMergedInto);
+
+      // console.log('Direct merges',
+      //     history.allDirectMergesInto(transaction.identifier));
+
+
+      return true;
+    },
+  },
+  methods: {
+    setTimelineTime(e, timestamp) {
+      e.preventDefault();
+      e.stopPropagation();
+      this.$store.dispatch('updateTimelineTime', timestamp);
+    },
+    transactionTypeOf(transaction) {
+      if (transaction.type !== 'transaction') {
+        return transaction.type;
+      }
+
+      if (transaction.transactions.length === 0) {
+        return 'Empty Transaction';
+      }
+
+      const types = new Set();
+      transaction.transactions.forEach((t) => types.add(t.type));
+
+      return Array.from(types).join(', ');
+    },
+    sufacesAffectedBy(transaction) {
+      if (transaction.type !== 'transaction') {
+        // TODO (b/162402459): Shorten layer name
+        return [{name: transaction.layerName, id: transaction.obj.id}];
+      }
+
+      const surfaceIds = new Set();
+      const affectedSurfaces = [];
+      for (const transaction of transaction.transactions) {
+        const id = transaction.obj.id;
+        if (!surfaceIds.has(id)) {
+          surfaceIds.add(id);
+          affectedSurfaces.push({name: transaction.layerName, id});
+        }
+      }
+
+      return affectedSurfaces;
+    },
+  },
+};
+</script>
+<style scoped>
+.time-column {
+  display: inline-flex;
+  width: 13em;
+}
+
+.time-column .time-link {
+  width: 9em;
+}
+
+.type-column {
+  width: 12em;
+}
+
+.origin-column {
+  width: 9em;
+}
+
+.affected-surfaces-column {
+  word-wrap: break-word;
+  width: 30em;
+}
+
+.extra-info-column {
+  width: 20em;
+}
+
+.entry {
+  display: inline-flex;
+  cursor: pointer;
+}
+
+.entry > div {
+  padding: 6px 10px;
+  border-bottom: 1px solid #f1f1f1;
+}
+
+.entry.selected {
+  background-color: #365179;
+  color: white;
+}
+
+.entry.selected a {
+  color: white;
+}
+
+.entry:not(.selected):hover {
+  background: #f1f1f1;
+}
+
+a {
+  cursor: pointer;
+}
+
+.inactive {
+  color: gray;
+}
+
+.inactive a {
+  color: gray;
+}
+
+.new-badge {
+  display: inline-block;
+  background: rgb(84, 139, 247);
+  border-radius: 3px;
+  color: white;
+  padding: 0 5px;
+  margin-left: 5px;
+  font-size: 10px;
+}
+
+.affected-surfaces-column .surface-id {
+  color: #999999
+}
+
+.inactive .affected-surfaces-column .surface-id {
+  color: #b4b4b4
+}
+
+.light {
+  color: #999999
+}
+
+.inactive .light {
+  color: #b4b4b4
+}
+
+.vsync {
+  position: relative;
+}
+
+.vsync-dot:before {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: -5px;
+  height: 10px;
+  width: 10px;
+  background-color: rgb(170, 65, 255);
+  border-radius: 50%;
+  display: inline-block;
+}
+
+.vsync-dot:after {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  height: 1px;
+  width: 100%;
+  background-color: rgb(170, 65, 255);
+  display: inline-block;
+}
+</style>
diff --git a/tools/winscope/src/TransactionsView.vue b/tools/winscope/src/TransactionsView.vue
new file mode 100644
index 0000000..84ea3db
--- /dev/null
+++ b/tools/winscope/src/TransactionsView.vue
@@ -0,0 +1,523 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<template>
+  <md-card-content class="container">
+
+    <flat-card class="changes card">
+      <div class="filters">
+        <div class="input">
+          <md-field>
+            <label>Transaction Type</label>
+            <md-select v-model="selectedTransactionTypes" multiple>
+              <md-option
+                v-for="type in transactionTypes"
+                :value="type"
+                v-bind:key="type">
+                {{ type }}
+              </md-option>
+            </md-select>
+          </md-field>
+        </div>
+
+        <div class="input">
+          <div>
+            <md-autocomplete
+              v-model="selectedProperty"
+              :md-options="properties"
+            >
+              <label>Changed property</label>
+            </md-autocomplete>
+            <!-- TODO(b/159582192): Add way to select value a property has
+              changed to, figure out how to handle properties that are
+              objects... -->
+          </div>
+        </div>
+
+        <div class="input">
+          <md-field>
+            <label>Origin PID</label>
+            <md-select v-model="selectedPids" multiple>
+              <md-option v-for="pid in pids" :value="pid" v-bind:key="pid">
+                {{ pid }}
+              </md-option>
+            </md-select>
+          </md-field>
+        </div>
+
+        <div class="input">
+          <md-field>
+            <label>Origin UID</label>
+            <md-select v-model="selectedUids" multiple>
+              <md-option v-for="uid in uids" :value="uid" v-bind:key="uid">
+                {{ uid }}
+              </md-option>
+            </md-select>
+          </md-field>
+        </div>
+
+        <div class="input">
+          <md-chips
+            v-model="filters"
+            md-placeholder="Add surface id or name..."
+          >
+            <div class="md-helper-text">Press enter to add</div>
+          </md-chips>
+        </div>
+      </div>
+
+      <virtual-list style="height: 600px; overflow-y: auto;"
+        :data-key="'timestamp'"
+        :data-sources="filteredData"
+        :data-component="transactionEntryComponent"
+        :extra-props="{
+          onClick: transactionSelected,
+          selectedTransaction,
+          transactionsTrace,
+          prettifyTransactionId,
+        }"
+        ref="loglist"
+      />
+    </flat-card>
+
+    <flat-card class="changes card">
+      <md-content
+        md-tag="md-toolbar"
+        md-elevation="0"
+        class="card-toolbar md-transparent md-dense"
+      >
+        <h2 class="md-title" style="flex: 1">Changes</h2>
+      </md-content>
+      <div class="changes-content" v-if="selectedTree">
+        <div
+          v-if="selectedTransaction.type === 'transaction'"
+          class="transaction-events"
+        >
+          <div
+            v-for="(event, i) in transactionHistory(selectedTransaction)"
+            v-bind:key="`${selectedTransaction.identifier}-${i}`"
+            class="transaction-event"
+          >
+            <div v-if="event.type === 'apply'" class="applied-event">
+              applied
+            </div>
+            <div v-if="event.type === 'merge'" class="merged-event">
+              <!-- eslint-disable-next-line max-len -->
+              {{ prettifyTransactionId(event.mergedId) }}
+            </div>
+          </div>
+        </div>
+        <tree-view
+          :item="selectedTree"
+          :collapseChildren="true"
+          :useGlobalCollapsedState="true"
+        />
+      </div>
+      <div class="no-properties" v-else>
+        <i class="material-icons none-icon">
+          filter_none
+        </i>
+        <span>No transaction selected.</span>
+      </div>
+    </flat-card>
+
+  </md-card-content>
+</template>
+<script>
+import TreeView from './TreeView.vue';
+import VirtualList from '../libs/virtualList/VirtualList';
+import TransactionEntry from './TransactionEntry.vue';
+import FlatCard from './components/FlatCard.vue';
+
+import {ObjectTransformer} from './transform.js';
+import {expandTransactionId} from '@/traces/Transactions.ts';
+
+export default {
+  name: 'transactionsview',
+  props: ['trace'],
+  data() {
+    const transactionTypes = new Set();
+    const properties = new Set();
+    const pids = new Set();
+    const uids = new Set();
+    const transactionsTrace = this.trace;
+    for (const entry of transactionsTrace.data) {
+      if (entry.type == 'transaction') {
+        for (const transaction of entry.transactions) {
+          transactionTypes.add(transaction.type);
+          Object.keys(transaction.obj).forEach((item) => properties.add(item));
+        }
+      } else {
+        transactionTypes.add(entry.type);
+        Object.keys(entry.obj).forEach((item) => properties.add(item));
+      }
+
+      if (entry.origin) {
+        pids.add(entry.origin.pid);
+        uids.add(entry.origin.uid);
+      }
+    }
+
+    // Remove vsync from being transaction types that can be filtered
+    // We want to always show vsyncs
+    transactionTypes.delete('vsyncEvent');
+
+    return {
+      transactionTypes: Array.from(transactionTypes),
+      properties: Array.from(properties),
+      pids: Array.from(pids),
+      uids: Array.from(uids),
+      selectedTransactionTypes: [],
+      selectedPids: [],
+      selectedUids: [],
+      searchInput: '',
+      selectedTree: null,
+      filters: [],
+      selectedProperty: null,
+      selectedTransaction: null,
+      transactionEntryComponent: TransactionEntry,
+      transactionsTrace,
+      expandTransactionId,
+    };
+  },
+  computed: {
+    data() {
+      return this.transactionsTrace.data;
+    },
+    filteredData() {
+      let filteredData = this.data;
+
+      if (this.selectedTransactionTypes.length > 0) {
+        filteredData = filteredData.filter(
+            this.filterTransactions((transaction) =>
+              transaction.type === 'vsyncEvent' ||
+              this.selectedTransactionTypes.includes(transaction.type)));
+      }
+
+      if (this.selectedPids.length > 0) {
+        filteredData = filteredData.filter((entry) =>
+          this.selectedPids.includes(entry.origin?.pid));
+      }
+
+      if (this.selectedUids.length > 0) {
+        filteredData = filteredData.filter((entry) =>
+          this.selectedUids.includes(entry.origin?.uid));
+      }
+
+      if (this.filters.length > 0) {
+        filteredData = filteredData.filter(
+            this.filterTransactions((transaction) => {
+              for (const filter of this.filters) {
+                if (isNaN(filter) && transaction.layerName?.includes(filter)) {
+                // If filter isn't a number then check if the transaction's
+                // target surface's name matches the filter — if so keep it.
+                  return true;
+                }
+                if (filter == transaction.obj.id) {
+                // If filteter is a number then check if the filter matches
+                // the transaction's target surface id — if so keep it.
+                  return true;
+                }
+              }
+
+              // Exclude transaction if it fails to match filter.
+              return false;
+            }),
+        );
+      }
+
+      if (this.selectedProperty) {
+        filteredData = filteredData.filter(
+            this.filterTransactions((transaction) => {
+              for (const key in transaction.obj) {
+                if (this.isMeaningfulChange(transaction.obj, key) &&
+                    key === this.selectedProperty) {
+                  return true;
+                }
+              }
+
+              return false;
+            }),
+        );
+      }
+
+      // We quish vsyncs because otherwise the lazy list will not load enough
+      // elements if there are many vsyncs in a row since vsyncs take up no
+      // space.
+      return this.squishVSyncs(filteredData);
+    },
+
+  },
+  methods: {
+    removeNullFields(changeObject) {
+      for (const key in changeObject) {
+        if (changeObject[key] === null) {
+          delete changeObject[key];
+        }
+      }
+
+      return changeObject;
+    },
+    transactionSelected(transaction) {
+      this.selectedTransaction = transaction;
+
+      const META_DATA_KEY = 'metadata';
+
+      let obj;
+      let name;
+      if (transaction.type == 'transaction') {
+        name = 'changes';
+        obj = {};
+
+        const [surfaceChanges, displayChanges] =
+          this.aggregateTransactions(transaction.transactions);
+
+        // Prepare the surface and display changes to be passed through
+        // the ObjectTransformer — in particular, remove redundant properties
+        // and add metadata that can be accessed post transformation
+        const perpareForTreeViewTransform = (change) => {
+          this.removeNullFields(change);
+          change[META_DATA_KEY] = {
+            // TODO (b/162402459): Shorten layer name
+            layerName: change.layerName,
+          };
+          // remove redundant properties
+          delete change.layerName;
+          delete change.id;
+        };
+
+        for (const changeId in surfaceChanges) {
+          if (surfaceChanges.hasOwnProperty(changeId)) {
+            perpareForTreeViewTransform(surfaceChanges[changeId]);
+          }
+        }
+        for (const changeId in displayChanges) {
+          if (displayChanges.hasOwnProperty(changeId)) {
+            perpareForTreeViewTransform(displayChanges[changeId]);
+          }
+        }
+
+        if (Object.keys(surfaceChanges).length > 0) {
+          obj.surfaceChanges = surfaceChanges;
+        }
+
+        if (Object.keys(displayChanges).length > 0) {
+          obj.displayChanges = displayChanges;
+        }
+      } else {
+        obj = this.removeNullFields(transaction.obj);
+        name = transaction.type;
+      }
+
+      // Transform the raw JS object to be TreeView compatible
+      const transactionUniqueId = transaction.timestamp;
+      let tree = new ObjectTransformer(
+          obj,
+          name,
+          transactionUniqueId,
+      ).setOptions({
+        formatter: () => {},
+      }).transform({
+        keepOriginal: true,
+        metadataKey: META_DATA_KEY,
+        freeze: false,
+      });
+
+      // Add the layer name as the kind of the object to be shown in the
+      // TreeView
+      const addLayerNameAsKind = (tree) => {
+        for (const layerChanges of tree.children) {
+          layerChanges.kind = layerChanges.metadata.layerName;
+        }
+      };
+
+      if (transaction.type == 'transaction') {
+        for (const child of tree.children) {
+          // child = surfaceChanges or displayChanges tree node
+          addLayerNameAsKind(child);
+        }
+      }
+
+      // If there are only surfaceChanges or only displayChanges and not both
+      // remove the extra top layer node which is meant to hold both types of
+      // changes when both are present
+      if (tree.name == 'changes' && tree.children.length === 1) {
+        tree = tree.children[0];
+      }
+
+      this.selectedTree = tree;
+    },
+    filterTransactions(condition) {
+      return (entry) => {
+        if (entry.type == 'transaction') {
+          for (const transaction of entry.transactions) {
+            if (condition(transaction)) {
+              return true;
+            }
+          }
+
+          return false;
+        } else {
+          return condition(entry);
+        }
+      };
+    },
+    isMeaningfulChange(object, key) {
+      // TODO (b/159799733): Handle cases of non null objects but meaningless
+      // change
+      return object[key] !== null && object.hasOwnProperty(key);
+    },
+    mergeChanges(a, b) {
+      const res = {};
+
+      for (const key in a) {
+        if (this.isMeaningfulChange(a, key)) {
+          res[key] = a[key];
+        }
+      }
+
+      for (const key in b) {
+        if (this.isMeaningfulChange(b, key)) {
+          if (res.hasOwnProperty(key) && key != 'id') {
+            throw new Error(`Merge failed – key '${key}' already present`);
+          }
+          res[key] = b[key];
+        }
+      }
+
+      return res;
+    },
+    aggregateTransactions(transactions) {
+      const surfaceChanges = {};
+      const displayChanges = {};
+
+      for (const transaction of transactions) {
+        const obj = transaction.obj;
+
+        // Create a new base object to merge all changes into
+        const newBaseObj = () => {
+          return {
+            layerName: transaction.layerName,
+          };
+        };
+
+        switch (transaction.type) {
+          case 'surfaceChange':
+            surfaceChanges[obj.id] =
+              this.mergeChanges(surfaceChanges[obj.id] ?? newBaseObj(), obj);
+            break;
+
+          case 'displayChange':
+            displayChanges[obj.id] =
+              this.mergeChanges(displayChanges[obj.id] ?? newBaseObj(), obj);
+            break;
+
+          default:
+            throw new Error(`Unhandled transaction type ${transaction.type}`);
+        }
+      }
+
+      return [surfaceChanges, displayChanges];
+    },
+
+    transactionHistory(selectedTransaction) {
+      const transactionId = selectedTransaction.identifier;
+      const history = this.transactionsTrace.transactionHistory
+          .generateHistoryTreesOf(transactionId);
+
+      return history;
+    },
+
+    prettifyTransactionId(transactionId) {
+      const expandedId = expandTransactionId(transactionId);
+      return `${expandedId.pid}.${expandedId.id}`;
+    },
+
+    squishVSyncs(data) {
+      return data.filter((event, i) => {
+        return !(event.type === 'vsyncEvent' &&
+          data[i + 1]?.type === 'vsyncEvent');
+      });
+    },
+  },
+  components: {
+    'virtual-list': VirtualList,
+    'tree-view': TreeView,
+    'flat-card': FlatCard,
+  },
+};
+
+</script>
+<style scoped>
+.container {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.transaction-table,
+.changes {
+  flex: 1 1 0;
+  width: 0;
+  margin: 8px;
+}
+
+.scrollBody {
+  width: 100%;
+  height: 100%;
+  overflow: scroll;
+}
+
+.filters {
+  margin-bottom: 15px;
+  width: 100%;
+  padding: 15px 5px;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.filters .input {
+  max-width: 300px;
+  margin: 0 10px;
+  flex-grow: 1;
+}
+
+.changes-content {
+  padding: 18px;
+  height: 550px;
+  overflow: auto;
+}
+
+.no-properties {
+  display: flex;
+  flex-direction: column;
+  align-self: center;
+  align-items: center;
+  justify-content: center;
+  height: calc(100% - 50px);
+  padding: 50px 25px;
+}
+
+.no-properties .none-icon {
+  font-size: 35px;
+  margin-bottom: 10px;
+}
+
+.no-properties span {
+  font-weight: 100;
+}
+
+.transaction-event {
+  display: inline-flex;
+}
+</style>
diff --git a/tools/winscope/src/TreeView.vue b/tools/winscope/src/TreeView.vue
index 9445c52..929240e 100644
--- a/tools/winscope/src/TreeView.vue
+++ b/tools/winscope/src/TreeView.vue
@@ -13,79 +13,283 @@
      limitations under the License.
 -->
 <template>
-  <div class="tree-view">
-    <div @click="clicked" :class="computedClass">
-      <span class="kind">{{item.kind}}</span><span v-if="item.kind && item.name"> - </span><span>{{item.name}}</span>
-      <div v-for="c in item.chips" :title="c.long" :class="chipClassForChip(c)">
-        {{c.short}}
+  <div class="tree-view" v-if="item">
+    <div class="node"
+      :class="[{
+        leaf: isLeaf,
+        selected: isSelected,
+        'child-selected': immediateChildSelected,
+        clickable: isClickable,
+        hover: nodeHover,
+        'child-hover': childHover,
+      }, diffClass]"
+      :style="nodeOffsetStyle"
+      @click="clicked"
+      @contextmenu.prevent="openContextMenu"
+      ref="node"
+    >
+      <button
+        class="toggle-tree-btn"
+        @click="toggleTree"
+        v-if="!isLeaf"
+        v-on:click.stop
+      >
+        <i aria-hidden="true" class="md-icon md-theme-default material-icons">
+          {{isCollapsed ? "chevron_right" : "expand_more"}}
+        </i>
+      </button>
+      <div class="leaf-node-icon-wrapper" v-else>
+        <i class="leaf-node-icon"/>
+      </div>
+      <div class="description">
+        <div v-if="elementView">
+          <component
+            :is="elementView"
+            :item="item"
+            :simplify-names="simplifyNames"
+          />
+        </div>
+        <div v-else>
+          <DefaultTreeElement :item="item" :simplify-names="simplifyNames"/>
+        </div>
+      </div>
+      <div v-show="isCollapsed">
+        <button
+          class="expand-tree-btn"
+          :class="[{
+            'child-selected': isCollapsed && childIsSelected
+            }, collapseDiffClass]"
+          v-if="children"
+          @click="expandTree"
+          v-on:click.stop
+        >
+          <i
+            aria-hidden="true"
+            class="md-icon md-theme-default material-icons"
+          >
+            more_horiz
+          </i>
+        </button>
       </div>
     </div>
-    <div class="children" v-if="children">
-      <tree-view v-for="(c,i) in children" :item="c" @item-selected="childItemSelected" :selected="selected" :key="i" :chip-class='chipClass' :filter="childFilter(c)" :flattened="flattened" :force-flattened="applyingFlattened" v-show="filterMatches(c)" ref='children' />
+
+    <node-context-menu
+      ref="nodeContextMenu"
+      v-on:collapseAllOtherNodes="collapseAllOtherNodes"
+    />
+
+    <div class="children" v-if="children" v-show="!isCollapsed">
+      <tree-view
+        v-for="(c,i) in children"
+        :item="c"
+        @item-selected="childItemSelected"
+        :selected="selected"
+        :key="i"
+        :filter="childFilter(c)"
+        :flattened="flattened"
+        :simplify-names="simplifyNames"
+        :force-flattened="applyingFlattened"
+        v-show="filterMatches(c)"
+        :items-clickable="itemsClickable"
+        :initial-depth="depth + 1"
+        :collapse="collapseChildren"
+        :collapseChildren="collapseChildren"
+        :useGlobalCollapsedState="useGlobalCollapsedState"
+        v-on:hoverStart="childHover = true"
+        v-on:hoverEnd="childHover = false"
+        v-on:selected="immediateChildSelected = true"
+        v-on:unselected="immediateChildSelected = false"
+        :elementView="elementView"
+        v-on:collapseSibbling="collapseSibbling"
+        v-on:collapseAllOtherNodes="collapseAllOtherNodes"
+        v-on:closeAllContextMenus="closeAllContextMenus"
+        ref="children"
+      />
     </div>
   </div>
 </template>
-<script>
-import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
-import protobuf from 'protobufjs'
 
-var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs);
+<script>
+import DefaultTreeElement from './DefaultTreeElement.vue';
+import NodeContextMenu from './NodeContextMenu.vue';
+
+import {DiffType} from './utils/diff.js';
+
+/* in px, must be kept in sync with css, maybe find a better solution... */
+const levelOffset = 24;
 
 export default {
   name: 'tree-view',
-  props: ['item', 'selected', 'chipClass', 'filter', 'flattened', 'force-flattened'],
+  props: [
+    'item',
+    'selected',
+    'filter',
+    'simplify-names',
+    'flattened',
+    'force-flattened',
+    'items-clickable',
+    'initial-depth',
+    'collapse',
+    'collapseChildren',
+    // Allows collapse state to be tracked by Vuex so that collapse state of
+    // items with same stableId can remain consisten accross time and easily
+    // toggled from anywhere in the app.
+    // Should be true if you are using the same TreeView to display multiple
+    // trees throughout the component's lifetime to make sure same nodes are
+    // toggled when switching back and forth between trees.
+    // If true, requires all nodes in tree to have a stableId.
+    'useGlobalCollapsedState',
+    // Custom view to use to render the elements in the tree view
+    'elementView',
+  ],
   data() {
-    return {};
+    const isCollapsedByDefault = this.collapse ?? false;
+
+    return {
+      isChildSelected: false,
+      immediateChildSelected: false,
+      clickTimeout: null,
+      isCollapsedByDefault,
+      localCollapsedState: isCollapsedByDefault,
+      collapseDiffClass: null,
+      nodeHover: false,
+      childHover: false,
+      diffSymbol: {
+        [DiffType.NONE]: '',
+        [DiffType.ADDED]: '+',
+        [DiffType.DELETED]: '-',
+        [DiffType.MODIFIED]: '.',
+        [DiffType.MOVED]: '.',
+      },
+    };
+  },
+  watch: {
+    stableId() {
+      // Update anything that is required to change when item changes.
+      this.updateCollapsedDiffClass();
+    },
+    hasDiff(hasDiff) {
+      if (!hasDiff) {
+        this.collapseDiffClass = null;
+      } else {
+        this.updateCollapsedDiffClass();
+      }
+    },
+    currentTimestamp() {
+      // Update anything that is required to change when time changes.
+      this.updateCollapsedDiffClass();
+    },
+    isSelected(isSelected) {
+      if (isSelected) {
+        this.$emit('selected');
+      } else {
+        this.$emit('unselected');
+      }
+    },
   },
   methods: {
-    selectNext(found, parent) {
-      if (found && this.filterMatches(this.item)) {
-        this.clicked();
+    setCollapseValue(isCollapsed) {
+      if (this.useGlobalCollapsedState) {
+        this.$store.commit('setCollapsedState', {
+          item: this.item,
+          isCollapsed,
+        });
+      } else {
+        this.localCollapsedState = isCollapsed;
+      }
+    },
+    toggleTree() {
+      this.setCollapseValue(!this.isCollapsed);
+    },
+    expandTree() {
+      this.setCollapseValue(false);
+    },
+    selectNext(found, inCollapsedTree) {
+      // Check if this is the next visible item
+      if (found && this.filterMatches(this.item) && !inCollapsedTree) {
+        this.select();
         return false;
       }
-      if (this.selected === this.item) {
+
+      // Set traversal state variables
+      if (this.isSelected) {
         found = true;
       }
+      if (this.isCollapsed) {
+        inCollapsedTree = true;
+      }
+
+      // Travers children trees recursively in reverse to find currently
+      // selected item and select the next visible one
       if (this.$refs.children) {
-        for (var c of this.$refs.children) {
-          found = c.selectNext(found);
+        for (const c of this.$refs.children) {
+          found = c.selectNext(found, inCollapsedTree);
         }
       }
+
       return found;
     },
-    selectPrev(found) {
+    selectPrev(found, inCollapsedTree) {
+      // Set inCollapseTree flag to make sure elements in collapsed trees are
+      // not selected.
+      const isRootCollapse = !inCollapsedTree && this.isCollapsed;
+      if (isRootCollapse) {
+        inCollapsedTree = true;
+      }
+
+      // Travers children trees recursively in reverse to find currently
+      // selected item and select the previous visible one
       if (this.$refs.children) {
-        for (var c of [...this.$refs.children].reverse()) {
-          found = c.selectPrev(found);
+        for (const c of [...this.$refs.children].reverse()) {
+          found = c.selectPrev(found, inCollapsedTree);
         }
       }
-      if (found && this.filterMatches(this.item)) {
-        this.clicked();
+
+      // Unset inCollapseTree flag as we are no longer in a collapsed tree.
+      if (isRootCollapse) {
+        inCollapsedTree = false;
+      }
+
+      // Check if this is the previous visible item
+      if (found && this.filterMatches(this.item) && !inCollapsedTree) {
+        this.select();
         return false;
       }
-      if (this.selected === this.item) {
+
+      // Set found flag so that the next visited visible item can be selected.
+      if (this.isSelected) {
         found = true;
       }
+
       return found;
     },
     childItemSelected(item) {
+      this.isChildSelected = true;
       this.$emit('item-selected', item);
     },
-    clicked() {
+    select() {
       this.$emit('item-selected', this.item);
     },
-    chipClassForChip(c) {
-      return ['tree-view-internal-chip', this.chipClassOrDefault,
-        this.chipClassOrDefault + '-' + (c.class || 'default')
-      ];
+    clicked(e) {
+      if (window.getSelection().type === 'range') {
+        // Ignore click if is selection
+        return;
+      }
+
+      if (!this.isLeaf && e.detail % 2 === 0) {
+        // Double click collaspable node
+        this.toggleTree();
+      } else {
+        this.select();
+      }
     },
     filterMatches(c) {
-      // If a filter is set, consider the item matches if the current item or any of its
-      // children matches.
+      // If a filter is set, consider the item matches if the current item or
+      // any of its children matches.
       if (this.filter) {
-        var thisMatches = this.filter(c);
+        const thisMatches = this.filter(c);
         const childMatches = (child) => this.filterMatches(child);
-        return thisMatches || (!this.applyingFlattened && 
+        return thisMatches || (!this.applyingFlattened &&
             c.children && c.children.some(childMatches));
       }
       return true;
@@ -99,71 +303,357 @@
       }
       return this.filter;
     },
+    isCurrentSelected() {
+      return this.selected === this.item;
+    },
+    updateCollapsedDiffClass() {
+      // NOTE: Could be memoized in $store map like collapsed state if
+      // performance ever becomes a problem.
+      if (this.item) {
+        this.collapseDiffClass = this.computeCollapseDiffClass();
+      }
+    },
+    getAllDiffTypesOfChildren(item) {
+      if (!item.children) {
+        return new Set();
+      }
+
+      const classes = new Set();
+      for (const child of item.children) {
+        if (child.diff) {
+          classes.add(child.diff.type);
+        }
+        for (const diffClass of this.getAllDiffTypesOfChildren(child)) {
+          classes.add(diffClass);
+        }
+      }
+
+      return classes;
+    },
+    computeCollapseDiffClass() {
+      if (!this.isCollapsed) {
+        return '';
+      }
+
+      const childrenDiffClasses = this.getAllDiffTypesOfChildren(this.item);
+
+      childrenDiffClasses.delete(DiffType.NONE);
+      childrenDiffClasses.delete(undefined);
+
+      if (childrenDiffClasses.size === 0) {
+        return '';
+      }
+      if (childrenDiffClasses.size === 1) {
+        const diff = childrenDiffClasses.values().next().value;
+        return diff;
+      }
+
+      return DiffType.MODIFIED;
+    },
+    collapseAllOtherNodes() {
+      this.$emit('collapseAllOtherNodes');
+      this.$emit('collapseSibbling', this.item);
+    },
+    collapseSibbling(item) {
+      if (!this.$refs.children) {
+        return;
+      }
+
+      for (const child of this.$refs.children) {
+        if (child.item === item) {
+          continue;
+        }
+
+        child.collapseAll();
+      }
+    },
+    collapseAll() {
+      this.setCollapseValue(true);
+
+      if (!this.$refs.children) {
+        return;
+      }
+
+      for (const child of this.$refs.children) {
+        child.collapseAll();
+      }
+    },
+    openContextMenu(e) {
+      this.closeAllContextMenus();
+      // vue-context takes in the event and uses clientX and clientY to
+      // determine the position of the context meny.
+      // This doesn't satisfy our use case so we specify our own positions for
+      // this.
+      this.$refs.nodeContextMenu.open({
+        clientX: e.x,
+        clientY: e.y,
+      });
+    },
+    closeAllContextMenus(requestOrigin) {
+      this.$refs.nodeContextMenu.close();
+      this.$emit('closeAllContextMenus', this.item);
+      this.closeAllChildrenContextMenus(requestOrigin);
+    },
+    closeAllChildrenContextMenus(requestOrigin) {
+      if (!this.$refs.children) {
+        return;
+      }
+
+      for (const child of this.$refs.children) {
+        if (child.item === requestOrigin) {
+          continue;
+        }
+
+        child.$refs.nodeContextMenu.close();
+        child.closeAllChildrenContextMenus();
+      }
+    },
   },
   computed: {
-    computedClass() {
-      return (this.item == this.selected) ? 'selected' : ''
+    hasDiff() {
+      return this.item?.diff !== undefined;
     },
-    chipClassOrDefault() {
-      return this.chipClass || 'tree-view-chip';
+    stableId() {
+      return this.item?.stableId;
+    },
+    currentTimestamp() {
+      return this.$store.state.currentTimestamp;
+    },
+    isCollapsed() {
+      if (!this.item.children || this.item.children?.length === 0) {
+        return false;
+      }
+
+      if (this.useGlobalCollapsedState) {
+        return this.$store.getters.collapsedStateStoreFor(this.item) ??
+        this.isCollapsedByDefault;
+      }
+
+      return this.localCollapsedState;
+    },
+    isSelected() {
+      return this.selected === this.item;
+    },
+    childIsSelected() {
+      if (this.$refs.children) {
+        for (const c of this.$refs.children) {
+          if (c.isSelected || c.childIsSelected) {
+            return true;
+          }
+        }
+      }
+
+      return false;
+    },
+    diffClass() {
+      return this.item.diff ? this.item.diff.type : '';
     },
     applyingFlattened() {
-      return this.flattened && this.item.flattened || this.forceFlattened;
+      return (this.flattened && this.item.flattened) || this.forceFlattened;
     },
     children() {
       return this.applyingFlattened ? this.item.flattened : this.item.children;
     },
-  }
-}
+    isLeaf() {
+      return !this.children || this.children.length === 0;
+    },
+    isClickable() {
+      return !this.isLeaf || this.itemsClickable;
+    },
+    depth() {
+      return this.initialDepth || 0;
+    },
+    nodeOffsetStyle() {
+      const offest = levelOffset * (this.depth + this.isLeaf) + 'px';
 
+      return {
+        marginLeft: '-' + offest,
+        paddingLeft: offest,
+      };
+    },
+  },
+  mounted() {
+    // Prevent highlighting on multiclick of node element
+    this.nodeMouseDownEventListner = (e) => {
+      if (e.detail > 1) {
+        e.preventDefault();
+        return false;
+      }
+
+      return true;
+    };
+    this.$refs.node?.addEventListener('mousedown',
+        this.nodeMouseDownEventListner);
+
+    this.updateCollapsedDiffClass();
+
+    this.nodeMouseEnterEventListener = (e) => {
+      this.nodeHover = true;
+      this.$emit('hoverStart');
+    };
+    this.$refs.node?.addEventListener('mouseenter',
+        this.nodeMouseEnterEventListener);
+
+    this.nodeMouseLeaveEventListener = (e) => {
+      this.nodeHover = false;
+      this.$emit('hoverEnd');
+    };
+    this.$refs.node?.addEventListener('mouseleave',
+        this.nodeMouseLeaveEventListener);
+  },
+  beforeDestroy() {
+    this.$refs.node?.removeEventListener('mousedown',
+        this.nodeMouseDownEventListner);
+    this.$refs.node?.removeEventListener('mouseenter',
+        this.nodeMouseEnterEventListener);
+    this.$refs.node?.removeEventListener('mouseleave',
+        this.nodeMouseLeaveEventListener);
+  },
+  components: {
+    DefaultTreeElement,
+    NodeContextMenu,
+  },
+};
 </script>
 <style>
+.data-card > .tree-view {
+  border: none;
+}
+
+.tree-view {
+  display: block;
+}
+
+.tree-view .node {
+  display: flex;
+  padding: 2px;
+  align-items: flex-start;
+}
+
+.tree-view .node.clickable {
+  cursor: pointer;
+}
+
+.tree-view .node:hover:not(.selected) {
+  background: #f1f1f1;
+}
+
+.tree-view .node:not(.selected).added,
+.tree-view .node:not(.selected).addedMove,
+.tree-view .expand-tree-btn.added,
+.tree-view .expand-tree-btn.addedMove {
+  background: #03ff35;
+}
+
+.tree-view .node:not(.selected).deleted,
+.tree-view .node:not(.selected).deletedMove,
+.tree-view .expand-tree-btn.deleted,
+.tree-view .expand-tree-btn.deletedMove {
+  background: #ff6b6b;
+}
+
+.tree-view .node:not(.selected).modified,
+.tree-view .expand-tree-btn.modified {
+  background: cyan;
+}
+
+.tree-view .node.addedMove:after,
+.tree-view .node.deletedMove:after {
+  content: 'moved';
+  margin: 0 5px;
+  background: #448aff;
+  border-radius: 5px;
+  padding: 3px;
+  color: white;
+}
+
 .children {
-  margin-left: 24px;
+  /* Aligns border with collapse arrows */
+  margin-left: 12px;
+  padding-left: 11px;
+  border-left: 1px solid rgb(238, 238, 238);
+  margin-top: 0px;
+}
+
+.tree-view .node.child-selected + .children {
+  border-left: 1px solid #b4b4b4;
+}
+
+.tree-view .node.selected + .children {
+  border-left: 1px solid rgb(200, 200, 200);
+}
+
+.tree-view .node.child-hover + .children {
+  border-left: 1px solid #b4b4b4;
+}
+
+.tree-view .node.hover + .children {
+  border-left: 1px solid rgb(200, 200, 200);
 }
 
 .kind {
   color: #333;
+  font-weight: bold;
 }
 
 .selected {
-  background-color: #3f51b5;
+  background-color: #365179;
   color: white;
 }
 
+.childSelected {
+  border-left: 1px solid rgb(233, 22, 22)
+}
+
 .selected .kind {
-  color: #ccc;
+  color: #e9e9e9;
 }
 
-.tree-view-internal-chip {
+.toggle-tree-btn, .expand-tree-btn {
+  background: none;
+  color: inherit;
+  border: none;
+  padding: 0;
+  font: inherit;
+  cursor: pointer;
+  outline: inherit;
+}
+
+.expand-tree-btn {
+  margin-left: 5px;
+}
+
+.expand-tree-btn.child-selected {
+  color: #3f51b5;
+}
+
+.description {
+  display: flex;
+  flex: 1 1 auto;
+}
+
+.description > div {
+  display: flex;
+  flex: 1 1 auto;
+}
+
+.leaf-node-icon-wrapper {
+  width: 24px;
+  height: 24px;
+  display: inline-flex;
+  align-content: center;
+  align-items: center;
+  justify-content: center;
+}
+
+.leaf-node-icon {
+  content: "";
   display: inline-block;
-}
-
-.tree-view-chip {
-  padding: 0 10px;
-  border-radius: 10px;
-  background-color: #aaa;
-  color: black;
-}
-
-.tree-view-chip.tree-view-chip-warn {
-  background-color: #ffaa6b;
-  color: black;
-}
-
-.tree-view-chip.tree-view-chip-error {
-  background-color: #ff6b6b;
-  color: black;
-}
-
-.tree-view-chip.tree-view-chip-gpu {
-  background-color: #00c853;
-  color: black;
-}
-
-.tree-view-chip.tree-view-chip-hwc {
-  background-color: #448aff;
-  color: black;
+  height: 5px;
+  width: 5px;
+  margin-top: -2px;
+  border-radius: 50%;
+  background-color: #9b9b9b;
 }
 
 </style>
diff --git a/tools/winscope/src/VideoView.vue b/tools/winscope/src/VideoView.vue
index 9b61493..4180767 100644
--- a/tools/winscope/src/VideoView.vue
+++ b/tools/winscope/src/VideoView.vue
@@ -13,63 +13,73 @@
      limitations under the License.
 -->
 <template>
-  <md-card-content class="container">
-    <md-card class="rects">
-      <md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
-        <h2 class="md-title">Screen</h2>
-      </md-whiteframe>
-      <md-whiteframe md-elevation="8">
-        <video :id="file.filename" class="screen" :src="file.data" />
-      </md-whiteframe>
-    </md-card>
-  </md-card-content>
+  <video
+    class="md-elevation-2 screen"
+    :src="file.data"
+    :style="style"
+    ref="video"
+  />
 </template>
 <script>
-const EPSILON = 0.00001
+const EPSILON = 0.00001;
 
 function uint8ToString(array) {
-  var chunk = 0x8000;
-  var out = [];
-  for (var i = 0; i < array.length; i += chunk) {
+  const chunk = 0x8000;
+  const out = [];
+  for (let i = 0; i < array.length; i += chunk) {
     out.push(String.fromCharCode.apply(null, array.subarray(i, i + chunk)));
   }
-  return out.join("");
+  return out.join('');
 }
 
 export default {
   name: 'videoview',
+  props: ['file', 'height'],
   data() {
-    return {}
+    return {};
+  },
+  computed: {
+    selectedIndex() {
+      return this.file.selectedIndex;
+    },
+    style() {
+      if (typeof this.height == 'number') {
+        return `height: ${this.height}px`;
+      } else {
+        return `height: ${this.height}`;
+      }
+    },
   },
   methods: {
     arrowUp() {
-      return true
+      return true;
     },
     arrowDown() {
       return true;
     },
+    selectFrameAtTime(timestamp) {
+      const time = (timestamp - this.file.timeline[0]) / 1000000000 + EPSILON;
+      this.$refs.video.currentTime = time;
+    },
     selectFrame(idx) {
-      var time = (this.file.timeline[idx] - this.file.timeline[0]) / 1000000000 + EPSILON;
-      document.getElementById(this.file.filename).currentTime = time;
+      this.selectFrameAtTime(this.file.timeline[idx]);
+    },
+    jumpToSelectedIndex() {
+      this.selectFrame(this.file.selectedIndex);
     },
   },
   watch: {
     selectedIndex() {
       this.selectFrame(this.file.selectedIndex);
-    }
-  },
-  props: ['file'],
-  computed: {
-    selectedIndex() {
-      return this.file.selectedIndex;
     },
   },
-}
+  mounted() {
+    this.$el.addEventListener('canplay', (e) => {
+      this.$emit('loaded');
+    });
+  },
+};
 
 </script>
 <style>
-.screen {
-  max-height: 50em;
-}
-
 </style>
diff --git a/tools/winscope/src/WindowManagerTraceView.vue b/tools/winscope/src/WindowManagerTraceView.vue
new file mode 100644
index 0000000..a03a95f
--- /dev/null
+++ b/tools/winscope/src/WindowManagerTraceView.vue
@@ -0,0 +1,35 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<template>
+  <TraceView :store="store" :file="file" :summarizer="summarizer" />
+</template>
+
+<script>
+import TraceView from "@/TraceView.vue"
+
+export default {
+  name: "WindowManagerTraceView",
+  props: ["store", "file"],
+  components: {
+    TraceView
+  },
+  methods: {
+    summarizer(item) {
+      return null;
+    },
+  }
+}
+</script>
\ No newline at end of file
diff --git a/tools/winscope/src/components/FlatCard.vue b/tools/winscope/src/components/FlatCard.vue
new file mode 100644
index 0000000..38c3356
--- /dev/null
+++ b/tools/winscope/src/components/FlatCard.vue
@@ -0,0 +1,38 @@
+<template>
+  <div class="flat-card">
+    <slot />
+  </div>
+</template>
+<script>
+export default {
+  name: 'FlatCard',
+}
+</script>
+<style scoped>
+.flat-card {
+  word-break: normal;
+  tab-size: 4;
+  font-size: 14px;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -webkit-tap-highlight-color: rgba(0,0,0,0);
+  font-family: Roboto, sans-serif;
+  line-height: 1.5;
+  background-repeat: no-repeat;
+  box-sizing: inherit;
+  padding: 0;
+  margin: 0;
+  display: block;
+  max-width: 100%;
+  outline: none;
+  text-decoration: none;
+  transition-property: box-shadow,opacity;
+  overflow-wrap: break-word;
+  position: relative;
+  white-space: normal;
+  border: thin solid rgba(0,0,0,.12);
+  background-color: #fff;
+  color: rgba(0,0,0,.87);
+  border-radius: 4px;
+}
+</style>
\ No newline at end of file
diff --git a/tools/winscope/src/components/IconSelection/IconSelectOption.vue b/tools/winscope/src/components/IconSelection/IconSelectOption.vue
new file mode 100644
index 0000000..8c428b8
--- /dev/null
+++ b/tools/winscope/src/components/IconSelection/IconSelectOption.vue
@@ -0,0 +1,164 @@
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<template>
+  <md-menu-item
+    :class="optionClasses"
+    :disabled="isDisabled" @click="setSelection"
+  >
+    <md-checkbox
+      class="md-primary"
+      v-model="isChecked"
+      v-if="MdSelect.multiple"
+      :disabled="isDisabled"
+    />
+
+    <div class="item">
+      <i class="material-icons" v-if="icon">
+        {{ icon }}
+      </i>
+      <span class="value">
+        {{ displayValue || value }}
+      </span>
+      <span class="material-icons help-icon">
+        help_outline
+        <md-tooltip md-direction="right">{{ desc }}</md-tooltip>
+      </span>
+    </div>
+  </md-menu-item>
+</template>
+
+<script>
+export default {
+  name: 'MdIconOption',
+  props: {
+    // Serves as key for option (should be unique within an MdSelect)
+    // Also serves as the backup to displayValue if null
+    value: [String, Number, Boolean],
+    // Value shown to describe an option in dropdown selection
+    displayValue: [String, Number, Boolean],
+    // If present, this is shown to represent item when dropdown is collapsed
+    shortValue: [String, Number, Boolean],
+    icon: String,
+    desc: String,
+    disabled: Boolean,
+  },
+  inject: {
+    MdSelect: {},
+    MdOptgroup: {
+      default: {},
+    },
+  },
+  data: () => ({
+    isSelected: false,
+    isChecked: false,
+  }),
+  computed: {
+    selectValue() {
+      return this.MdSelect.modelValue;
+    },
+    isMultiple() {
+      return this.MdSelect.multiple;
+    },
+    isDisabled() {
+      return this.MdOptgroup.disabled || this.disabled;
+    },
+    key() {
+      return this.value;
+    },
+    inputLabel() {
+      return this.MdSelect.label;
+    },
+    optionClasses() {
+      return {
+        'md-selected': this.isSelected || this.isChecked,
+      };
+    },
+  },
+  watch: {
+    selectValue() {
+      this.setIsSelected();
+    },
+    isChecked(val) {
+      if (val === this.isSelected) {
+        return;
+      }
+      this.setSelection();
+    },
+    isSelected(val) {
+      this.isChecked = val;
+    },
+  },
+  methods: {
+    getTextContent() {
+      return this.shortValue || this.displayValue || this.value;
+    },
+    setIsSelected() {
+      if (!this.isMultiple) {
+        this.isSelected = this.selectValue === this.value;
+        return;
+      }
+      if (this.selectValue === undefined) {
+        this.isSelected = false;
+        return;
+      }
+      this.isSelected = this.selectValue.includes(this.value);
+    },
+    setSingleSelection() {
+      this.MdSelect.setValue(this.value);
+    },
+    setMultipleSelection() {
+      this.MdSelect.setMultipleValue(this.value);
+    },
+    setSelection() {
+      if (!this.isDisabled) {
+        if (this.isMultiple) {
+          this.setMultipleSelection();
+        } else {
+          this.setSingleSelection();
+        }
+      }
+    },
+    setItem() {
+      this.$set(this.MdSelect.items, this.key, this.getTextContent());
+    },
+  },
+  updated() {
+    this.setItem();
+  },
+  created() {
+    this.setItem();
+    this.setIsSelected();
+  },
+};
+</script>
+
+<style scoped>
+.item {
+  display: inline-flex;
+  align-items: center;
+  width: 100%;
+  flex: 1;
+}
+
+.item .value {
+  flex-grow: 1;
+  margin: 0 10px;
+}
+
+.item .help-icon {
+  font-size: 15px;
+}
+</style>
diff --git a/tools/winscope/src/decode.js b/tools/winscope/src/decode.js
index 94a3de0..7dfbfa6 100644
--- a/tools/winscope/src/decode.js
+++ b/tools/winscope/src/decode.js
@@ -14,195 +14,403 @@
  * limitations under the License.
  */
 
+/* eslint-disable camelcase */
+/* eslint-disable max-len */
 
-import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
-import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/server/protolog.proto'
-import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto'
-import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto'
-import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto'
-import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto'
-import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto'
-import protobuf from 'protobufjs'
-import { transform_layers, transform_layers_trace } from './transform_sf.js'
-import { transform_window_service, transform_window_trace } from './transform_wm.js'
-import { transform_transaction_trace } from './transform_transaction.js'
-import { transform_wl_outputstate, transform_wayland_trace } from './transform_wl.js'
-import { transform_protolog } from './transform_protolog.js'
-import { transform_sysui_trace } from './transform_sys_ui.js'
-import { transform_launcher_trace } from './transform_launcher.js'
-import { fill_transform_data } from './matrix_utils.js'
-import { mp4Decoder } from './decodeVideo.js'
+import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
+import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
+import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
+import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto';
+import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto';
+import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto';
+import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
+import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
+import protobuf from 'protobufjs';
+import {transformLayers, transformLayersTrace} from './transform_sf.js';
+import {transform_transaction_trace} from './transform_transaction.js';
+import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
+import {transformProtolog} from './transform_protolog.js';
+import {transform_sysui_trace} from './transform_sys_ui.js';
+import {transform_launcher_trace} from './transform_launcher.js';
+import {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice} from './transform_ime.js';
+import {fill_transform_data} from './matrix_utils.js';
+import {mp4Decoder} from './decodeVideo.js';
 
-var WmTraceMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerTraceFileProto");
-var WmDumpMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerServiceDumpProto");
-var SfTraceMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersTraceFileProto");
-var SfDumpMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersProto");
-var SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, "Trace");
-var WaylandTraceMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.TraceFileProto");
-var WaylandDumpMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.OutputStateProto");
-var ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, "com.android.server.protolog.ProtoLogFileProto");
-var SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, "com.android.systemui.tracing.SystemUiTraceFileProto");
-var LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, "com.android.launcher3.tracing.LauncherTraceFileProto");
+import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
+import WindowManagerTrace from '@/traces/WindowManager.ts';
+import TransactionsTrace from '@/traces/Transactions.ts';
+import ScreenRecordingTrace from '@/traces/ScreenRecording.ts';
+import WaylandTrace from '@/traces/Wayland.ts';
+import ProtoLogTrace from '@/traces/ProtoLog.ts';
+import SystemUITrace from '@/traces/SystemUI.ts';
+import LauncherTrace from '@/traces/Launcher.ts';
+import ImeTraceClients from '@/traces/InputMethodClients.ts';
+import ImeTraceService from '@/traces/InputMethodService.ts';
+import ImeTraceManagerService from '@/traces/InputMethodManagerService.ts';
 
-const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45] // .LYRTRACE
-const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45] // .WINTRACE
-const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32] // ....ftypmp42
-const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45] // .WYLTRACE
-const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47] // .PROTOLOG
-const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43] // .SYSUITRC
-const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43] // .LNCHRTRC
+import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
+import WindowManagerDump from '@/dumps/WindowManager.ts';
+import WaylandDump from '@/dumps/Wayland.ts';
 
-const DATA_TYPES = {
-  WINDOW_MANAGER: {
-    name: "WindowManager",
-    icon: "view_compact",
-    mime: "application/octet-stream",
-  },
-  SURFACE_FLINGER: {
-    name: "SurfaceFlinger",
-    icon: "filter_none",
-    mime: "application/octet-stream",
-  },
-  SCREEN_RECORDING: {
-    name: "Screen recording",
-    icon: "videocam",
-    mime: "video/mp4",
-  },
-  TRANSACTION: {
-    name: "Transaction",
-    icon: "timeline",
-    mime: "application/octet-stream",
-  },
-  WAYLAND: {
-    name: "Wayland",
-    icon: "filter_none",
-    mime: "application/octet-stream",
-  },
-  PROTO_LOG: {
-    name: "ProtoLog",
-    icon: "notes",
-    mime: "application/octet-stream",
-  },
-  SYSTEM_UI: {
-    name: "SystemUI",
-    icon: "filter_none",
-    mime: "application/octet-stream",
-  },
-  LAUNCHER: {
-    name: "Launcher",
-    icon: "filter_none",
-    mime: "application/octet-stream",
-  },
+const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
+const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
+const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto');
+const SfDumpMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersProto');
+const SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, 'Trace');
+const WaylandTraceMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.TraceFileProto');
+const WaylandDumpMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.OutputStateProto');
+const ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
+const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto');
+const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto');
+const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodClientsTraceFileProto");
+const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodServiceTraceFileProto");
+const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodManagerServiceTraceFileProto");
+
+const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
+const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
+const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
+const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WYLTRACE
+const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
+const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43]; // .SYSUITRC
+const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43]; // .LNCHRTRC
+const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMCTRACE
+const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMSTRACE
+const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMMTRACE
+
+const FILE_TYPES = Object.freeze({
+  WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
+  SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
+  WINDOW_MANAGER_DUMP: 'WindowManagerDump',
+  SURFACE_FLINGER_DUMP: 'SurfaceFlingerDump',
+  SCREEN_RECORDING: 'ScreenRecording',
+  TRANSACTIONS_TRACE: 'TransactionsTrace',
+  WAYLAND_TRACE: 'WaylandTrace',
+  WAYLAND_DUMP: 'WaylandDump',
+  PROTO_LOG: 'ProtoLog',
+  SYSTEM_UI: 'SystemUI',
+  LAUNCHER: 'Launcher',
+  IME_TRACE_CLIENTS: 'ImeTraceClients',
+  IME_TRACE_SERVICE: 'ImeTrace InputMethodService',
+  IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
+});
+
+const WINDOW_MANAGER_ICON = 'view_compact';
+const SURFACE_FLINGER_ICON = 'filter_none';
+const SCREEN_RECORDING_ICON = 'videocam';
+const TRANSACTION_ICON = 'timeline';
+const WAYLAND_ICON = 'filter_none';
+const PROTO_LOG_ICON = 'notes';
+const SYSTEM_UI_ICON = 'filter_none';
+const LAUNCHER_ICON = 'filter_none';
+const IME_ICON = 'keyboard';
+
+const FILE_ICONS = {
+  [FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON,
+  [FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON,
+  [FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON,
+  [FILE_TYPES.SURFACE_FLINGER_DUMP]: SURFACE_FLINGER_ICON,
+  [FILE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
+  [FILE_TYPES.TRANSACTIONS_TRACE]: TRANSACTION_ICON,
+  [FILE_TYPES.WAYLAND_TRACE]: WAYLAND_ICON,
+  [FILE_TYPES.WAYLAND_DUMP]: WAYLAND_ICON,
+  [FILE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
+  [FILE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON,
+  [FILE_TYPES.LAUNCHER]: LAUNCHER_ICON,
+  [FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON,
+  [FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON,
+  [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: IME_ICON,
+};
+
+function oneOf(dataType) {
+  return {oneOf: true, type: dataType};
 }
 
-const FILE_TYPES = {
-  'window_trace': {
-    name: "WindowManager trace",
-    dataType: DATA_TYPES.WINDOW_MANAGER,
+function manyOf(dataType, fold = null) {
+  return {manyOf: true, type: dataType, fold};
+}
+
+const TRACE_TYPES = Object.freeze({
+  WINDOW_MANAGER: 'WindowManagerTrace',
+  SURFACE_FLINGER: 'SurfaceFlingerTrace',
+  SCREEN_RECORDING: 'ScreenRecording',
+  TRANSACTION: 'Transaction',
+  WAYLAND: 'Wayland',
+  PROTO_LOG: 'ProtoLog',
+  SYSTEM_UI: 'SystemUI',
+  LAUNCHER: 'Launcher',
+  IME_CLIENTS: 'ImeTrace Clients',
+  IME_SERVICE: 'ImeTrace InputMethodService',
+  IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
+});
+
+const TRACE_INFO = {
+  [TRACE_TYPES.WINDOW_MANAGER]: {
+    name: 'WindowManager',
+    icon: WINDOW_MANAGER_ICON,
+    files: [oneOf(FILE_TYPES.WINDOW_MANAGER_TRACE)],
+    constructor: WindowManagerTrace,
+  },
+  [TRACE_TYPES.SURFACE_FLINGER]: {
+    name: 'SurfaceFlinger',
+    icon: SURFACE_FLINGER_ICON,
+    files: [oneOf(FILE_TYPES.SURFACE_FLINGER_TRACE)],
+    constructor: SurfaceFlingerTrace,
+  },
+  [TRACE_TYPES.SCREEN_RECORDING]: {
+    name: 'Screen recording',
+    icon: SCREEN_RECORDING_ICON,
+    files: [oneOf(FILE_TYPES.SCREEN_RECORDING)],
+    constructor: ScreenRecordingTrace,
+  },
+  [TRACE_TYPES.TRANSACTION]: {
+    name: 'Transaction',
+    icon: TRANSACTION_ICON,
+    files: [
+      oneOf(FILE_TYPES.TRANSACTIONS_TRACE),
+    ],
+    constructor: TransactionsTrace,
+  },
+  [TRACE_TYPES.WAYLAND]: {
+    name: 'Wayland',
+    icon: WAYLAND_ICON,
+    files: [oneOf(FILE_TYPES.WAYLAND_TRACE)],
+    constructor: WaylandTrace,
+  },
+  [TRACE_TYPES.PROTO_LOG]: {
+    name: 'ProtoLog',
+    icon: PROTO_LOG_ICON,
+    files: [oneOf(FILE_TYPES.PROTO_LOG)],
+    constructor: ProtoLogTrace,
+  },
+  [TRACE_TYPES.SYSTEM_UI]: {
+    name: 'SystemUI',
+    icon: SYSTEM_UI_ICON,
+    files: [oneOf(FILE_TYPES.SYSTEM_UI)],
+    constructor: SystemUITrace,
+  },
+  [TRACE_TYPES.LAUNCHER]: {
+    name: 'Launcher',
+    icon: LAUNCHER_ICON,
+    files: [oneOf(FILE_TYPES.LAUNCHER)],
+    constructor: LauncherTrace,
+  },
+  [TRACE_TYPES.IME_CLIENTS]: {
+    name: 'InputMethodClients',
+    icon: IME_ICON,
+    files: [oneOf(FILE_TYPES.IME_TRACE_CLIENTS)],
+    constructor: ImeTraceClients,
+  },
+  [TRACE_TYPES.IME_SERVICE]: {
+    name: 'InputMethodService',
+    icon: IME_ICON,
+    files: [oneOf(FILE_TYPES.IME_TRACE_SERVICE)],
+    constructor: ImeTraceService,
+  },
+  [TRACE_TYPES.IME_MANAGERSERVICE]: {
+    name: 'InputMethodManagerService',
+    icon: IME_ICON,
+    files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)],
+    constructor: ImeTraceManagerService,
+  },
+};
+
+const DUMP_TYPES = Object.freeze({
+  WINDOW_MANAGER: 'WindowManagerDump',
+  SURFACE_FLINGER: 'SurfaceFlingerDump',
+  WAYLAND: 'WaylandDump',
+});
+
+const DUMP_INFO = {
+  [DUMP_TYPES.WINDOW_MANAGER]: {
+    name: 'WindowManager',
+    icon: WINDOW_MANAGER_ICON,
+    files: [oneOf(FILE_TYPES.WINDOW_MANAGER_DUMP)],
+    constructor: WindowManagerDump,
+  },
+  [DUMP_TYPES.SURFACE_FLINGER]: {
+    name: 'SurfaceFlinger',
+    icon: SURFACE_FLINGER_ICON,
+    files: [oneOf(FILE_TYPES.SURFACE_FLINGER_DUMP)],
+    constructor: SurfaceFlingerDump,
+  },
+  [DUMP_TYPES.WAYLAND]: {
+    name: 'Wayland',
+    icon: WAYLAND_ICON,
+    files: [oneOf(FILE_TYPES.WAYLAND_DUMP)],
+    constructor: WaylandDump,
+  },
+};
+
+export const TRACE_ICONS = {
+  [TRACE_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
+  [TRACE_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
+  [TRACE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
+  [TRACE_TYPES.TRANSACTION]: TRANSACTION_ICON,
+  [TRACE_TYPES.WAYLAND]: WAYLAND_ICON,
+  [TRACE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
+  [TRACE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON,
+  [TRACE_TYPES.LAUNCHER]: LAUNCHER_ICON,
+  [TRACE_TYPES.IME_CLIENTS]: IME_ICON,
+  [TRACE_TYPES.IME_SERVICE]: IME_ICON,
+  [TRACE_TYPES.IME_MANAGERSERVICE]: IME_ICON,
+
+  [DUMP_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
+  [DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
+  [DUMP_TYPES.WAYLAND]: WAYLAND_ICON,
+};
+
+// TODO: Rename name to defaultName
+const FILE_DECODERS = {
+  [FILE_TYPES.WINDOW_MANAGER_TRACE]: {
+    name: 'WindowManager trace',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.WINDOW_MANAGER_TRACE,
       protoType: WmTraceMessage,
-      transform: transform_window_trace,
+      transform: WindowManagerTrace.fromProto,
       timeline: true,
     },
   },
-  'layers_trace': {
-    name: "SurfaceFlinger trace",
-    dataType: DATA_TYPES.SURFACE_FLINGER,
+  [FILE_TYPES.SURFACE_FLINGER_TRACE]: {
+    name: 'SurfaceFlinger trace',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.SURFACE_FLINGER_TRACE,
+      mime: 'application/octet-stream',
       protoType: SfTraceMessage,
-      transform: transform_layers_trace,
+      transform: transformLayersTrace,
       timeline: true,
     },
   },
-  'wl_trace': {
-    name: "Wayland trace",
-    dataType: DATA_TYPES.WAYLAND,
+  [FILE_TYPES.WAYLAND_TRACE]: {
+    name: 'Wayland trace',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.WAYLAND_TRACE,
+      mime: 'application/octet-stream',
       protoType: WaylandTraceMessage,
       transform: transform_wayland_trace,
       timeline: true,
     },
   },
-  'layers_dump': {
-    name: "SurfaceFlinger dump",
-    dataType: DATA_TYPES.SURFACE_FLINGER,
+  [FILE_TYPES.SURFACE_FLINGER_DUMP]: {
+    name: 'SurfaceFlinger dump',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.SURFACE_FLINGER_DUMP,
+      mime: 'application/octet-stream',
       protoType: SfDumpMessage,
-      transform: (decoded) => transform_layers(true /*includesCompositionState*/, decoded),
+      transform: (decoded) => transformLayers(true /* includesCompositionState*/, decoded),
       timeline: false,
     },
   },
-  'window_dump': {
-    name: "WindowManager dump",
-    dataType: DATA_TYPES.WINDOW_MANAGER,
+  [FILE_TYPES.WINDOW_MANAGER_DUMP]: {
+    name: 'WindowManager dump',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.WINDOW_MANAGER_DUMP,
+      mime: 'application/octet-stream',
       protoType: WmDumpMessage,
-      transform: transform_window_service,
+      transform: WindowManagerDump.fromProto,
       timeline: false,
     },
   },
-  'wl_dump': {
-    name: "Wayland dump",
-    dataType: DATA_TYPES.WAYLAND,
+  [FILE_TYPES.WAYLAND_DUMP]: {
+    name: 'Wayland dump',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.WAYLAND_DUMP,
+      mime: 'application/octet-stream',
       protoType: WaylandDumpMessage,
       transform: transform_wl_outputstate,
       timeline: false,
     },
   },
-  'screen_recording': {
-    name: "Screen recording",
-    dataType: DATA_TYPES.SCREEN_RECORDING,
+  [FILE_TYPES.SCREEN_RECORDING]: {
+    name: 'Screen recording',
     decoder: videoDecoder,
     decoderParams: {
+      type: FILE_TYPES.SCREEN_RECORDING,
+      mime: 'video/mp4',
       videoDecoder: mp4Decoder,
     },
   },
-  'transaction': {
-    name: "Transaction",
-    dataType: DATA_TYPES.TRANSACTION,
+  [FILE_TYPES.TRANSACTIONS_TRACE]: {
+    name: 'Transaction',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.TRANSACTIONS_TRACE,
+      mime: 'application/octet-stream',
       protoType: SfTransactionTraceMessage,
       transform: transform_transaction_trace,
       timeline: true,
-    }
+    },
   },
-  'proto_log': {
-    name: "ProtoLog",
-    dataType: DATA_TYPES.PROTO_LOG,
+  [FILE_TYPES.PROTO_LOG]: {
+    name: 'ProtoLog',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.PROTO_LOG,
+      mime: 'application/octet-stream',
       protoType: ProtoLogMessage,
-      transform: transform_protolog,
+      transform: transformProtolog,
       timeline: true,
-    }
+    },
   },
-  'system_ui_trace': {
-    name: "SystemUI trace",
-    dataType: DATA_TYPES.SYSTEM_UI,
+  [FILE_TYPES.SYSTEM_UI]: {
+    name: 'SystemUI trace',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.SYSTEM_UI,
+      mime: 'application/octet-stream',
       protoType: SystemUiTraceMessage,
       transform: transform_sysui_trace,
       timeline: true,
-    }
+    },
   },
-  'launcher_trace': {
-    name: "Launcher trace",
-    dataType: DATA_TYPES.LAUNCHER,
+  [FILE_TYPES.LAUNCHER]: {
+    name: 'Launcher trace',
     decoder: protoDecoder,
     decoderParams: {
+      type: FILE_TYPES.LAUNCHER,
+      mime: 'application/octet-stream',
       protoType: LauncherTraceMessage,
       transform: transform_launcher_trace,
       timeline: true,
-    }
+    },
+  },
+  [FILE_TYPES.IME_TRACE_CLIENTS]: {
+    name: 'InputMethodClients trace',
+    decoder: protoDecoder,
+    decoderParams: {
+      type: FILE_TYPES.IME_TRACE_CLIENTS,
+      mime: 'application/octet-stream',
+      protoType: InputMethodClientsTraceMessage,
+      transform: transform_ime_trace_clients,
+      timeline: true,
+    },
+  },
+  [FILE_TYPES.IME_TRACE_SERVICE]: {
+    name: 'InputMethodService trace',
+    decoder: protoDecoder,
+    decoderParams: {
+      type: FILE_TYPES.IME_TRACE_SERVICE,
+      mime: 'application/octet-stream',
+      protoType: InputMethodServiceTraceMessage,
+      transform: transform_ime_trace_service,
+      timeline: true,
+    },
+  },
+  [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: {
+    name: 'InputMethodManagerService trace',
+    decoder: protoDecoder,
+    decoderParams: {
+      type: FILE_TYPES.IME_TRACE_MANAGERSERVICE,
+      mime: 'application/octet-stream',
+      protoType: InputMethodManagerServiceTraceMessage,
+      transform: transform_ime_trace_managerservice,
+      timeline: true,
+    },
   },
 };
 
@@ -218,58 +426,69 @@
   if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
     return;
   }
-  for (var fieldName in protoObj.$type.fields) {
-    var fieldProperties = protoObj.$type.fields[fieldName];
-    var field = protoObj[fieldName];
 
-    if (Array.isArray(field)) {
-      field.forEach((item, _) => {
-        modifyProtoFields(item, displayDefaults);
-      })
-      continue;
-    }
+  for (const fieldName in protoObj.$type.fields) {
+    if (protoObj.$type.fields.hasOwnProperty(fieldName)) {
+      const fieldProperties = protoObj.$type.fields[fieldName];
+      const field = protoObj[fieldName];
 
-    if (displayDefaults && !(field)) {
-      protoObj[fieldName] = fieldProperties.defaultValue;
-    }
+      if (Array.isArray(field)) {
+        field.forEach((item, _) => {
+          modifyProtoFields(item, displayDefaults);
+        });
+        continue;
+      }
 
-    if (fieldProperties.type === 'TransformProto') {
-      fill_transform_data(protoObj[fieldName]);
-      continue;
-    }
+      if (displayDefaults && !(field)) {
+        protoObj[fieldName] = fieldProperties.defaultValue;
+      }
 
-    if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
-      protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
-      continue;
+      if (fieldProperties.type === 'TransformProto') {
+        fill_transform_data(protoObj[fieldName]);
+        continue;
+      }
+
+      if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
+        protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
+        continue;
+      }
+      modifyProtoFields(protoObj[fieldName], displayDefaults);
     }
-    modifyProtoFields(protoObj[fieldName], displayDefaults);
   }
 }
 
-function protoDecoder(buffer, fileType, fileName, store) {
-  var decoded = fileType.decoderParams.protoType.decode(buffer);
-  modifyProtoFields(decoded, store.displayDefaults);
-  var transformed = fileType.decoderParams.transform(decoded);
-  var data
-  if (fileType.decoderParams.timeline) {
-    data = transformed.children;
+function decodeAndTransformProto(buffer, params, displayDefaults) {
+  const decoded = params.protoType.decode(buffer);
+  modifyProtoFields(decoded, displayDefaults);
+  const transformed = params.transform(decoded);
+
+  return transformed;
+}
+
+function protoDecoder(buffer, params, fileName, store) {
+  const transformed = decodeAndTransformProto(buffer, params, store.displayDefaults);
+  let data;
+  if (params.timeline) {
+    data = transformed.entries ?? transformed.children;
   } else {
     data = [transformed];
   }
-  let blobUrl = URL.createObjectURL(new Blob([buffer], { type: fileType.dataType.mime }));
-  return dataFile(fileName, data.map(x => x.timestamp), data, blobUrl, fileType.dataType);
+  const blobUrl = URL.createObjectURL(new Blob([buffer], {type: params.mime}));
+  return dataFile(fileName, data.map((x) => x.timestamp), data, blobUrl, params.type);
 }
 
-function videoDecoder(buffer, fileType, fileName, store) {
-  let [data, timeline] = fileType.decoderParams.videoDecoder(buffer);
-  let blobUrl = URL.createObjectURL(new Blob([data], { type: fileType.dataType.mime }));
-  return dataFile(fileName, timeline, blobUrl, blobUrl, fileType.dataType);
+function videoDecoder(buffer, params, fileName, store) {
+  const [data, timeline] = params.videoDecoder(buffer);
+  const blobUrl = URL.createObjectURL(new Blob([data], {type: params.mime}));
+  return dataFile(fileName, timeline, blobUrl, blobUrl, params.type);
 }
 
 function dataFile(filename, timeline, data, blobUrl, type) {
   return {
     filename: filename,
-    timeline: timeline,
+    // Object is frozen for performance reasons
+    // It will prevent Vue from making it a reactive object which will be very slow as the timeline gets larger.
+    timeline: Object.freeze(timeline),
     data: data,
     blobUrl: blobUrl,
     type: type,
@@ -277,14 +496,14 @@
     destroy() {
       URL.revokeObjectURL(this.blobUrl);
     },
-  }
+  };
 }
 
 function arrayEquals(a, b) {
   if (a.length !== b.length) {
     return false;
   }
-  for (var i = 0; i < a.length; i++) {
+  for (let i = 0; i < a.length; i++) {
     if (a[i] != b[i]) {
       return false;
     }
@@ -297,39 +516,70 @@
 }
 
 function decodedFile(fileType, buffer, fileName, store) {
-  return [fileType, fileType.decoder(buffer, fileType, fileName, store)];
+  const fileDecoder = FILE_DECODERS[fileType];
+  return [fileType, fileDecoder.decoder(buffer, fileDecoder.decoderParams, fileName, store)];
 }
 
 function detectAndDecode(buffer, fileName, store) {
   if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['layers_trace'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['window_trace'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) {
-    return decodedFile(FILE_TYPES['screen_recording'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.SCREEN_RECORDING, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['wl_trace'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.WAYLAND_TRACE, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['proto_log'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.PROTO_LOG, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['system_ui_trace'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.SYSTEM_UI, buffer, fileName, store);
   }
   if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) {
-    return decodedFile(FILE_TYPES['launcher_trace'], buffer, fileName, store);
+    return decodedFile(FILE_TYPES.LAUNCHER, buffer, fileName, store);
   }
-  for (var name of ['transaction', 'layers_dump', 'window_dump', 'wl_dump']) {
+  if (arrayStartsWith(buffer, IMC_TRACE_MAGIC_NUMBER)) {
+    return decodedFile(FILE_TYPES.IME_TRACE_CLIENTS, buffer, fileName, store);
+  }
+  if (arrayStartsWith(buffer, IMS_TRACE_MAGIC_NUMBER)) {
+    return decodedFile(FILE_TYPES.IME_TRACE_SERVICE, buffer, fileName, store);
+  }
+  if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) {
+    return decodedFile(FILE_TYPES.IME_TRACE_MANAGERSERVICE, buffer, fileName, store);
+  }
+
+  // TODO(b/169305853): Add magic number at beginning of file for better auto detection
+  for (const [filetype, condition] of [
+    [FILE_TYPES.TRANSACTIONS_TRACE, (file) => file.data.length > 0],
+    [FILE_TYPES.WAYLAND_DUMP, (file) => (file.data.length > 0 && file.data.children[0] > 0) || file.data.length > 1],
+    [FILE_TYPES.WINDOW_MANAGER_DUMP],
+    [FILE_TYPES.SURFACE_FLINGER_DUMP],
+  ]) {
     try {
-      return decodedFile(FILE_TYPES[name], buffer, fileName, store);
+      const [, fileData] = decodedFile(filetype, buffer, fileName, store);
+
+      // A generic file will often wrongly be decoded as an empty wayland dump file
+      if (condition && !condition(fileData)) {
+        // Fall through to next filetype
+        continue;
+      }
+
+      return [filetype, fileData];
     } catch (ex) {
-      // ignore exception and try next filetype
+      // ignore exception and fall through to next filetype
     }
   }
-  throw new Error('Unable to detect file');
+  throw new UndetectableFileType('Unable to detect file');
 }
 
-export { detectAndDecode, DATA_TYPES, FILE_TYPES };
+/**
+ * Error is raised when detectAndDecode is called but the file can't be
+ * automatically detected as being of a compatible file type.
+ */
+class UndetectableFileType extends Error { }
+
+export {detectAndDecode, decodeAndTransformProto, FILE_TYPES, TRACE_INFO, TRACE_TYPES, DUMP_TYPES, DUMP_INFO, FILE_DECODERS, FILE_ICONS, UndetectableFileType};
diff --git a/tools/winscope/src/dumps/DumpBase.ts b/tools/winscope/src/dumps/DumpBase.ts
new file mode 100644
index 0000000..9e2bd14
--- /dev/null
+++ b/tools/winscope/src/dumps/DumpBase.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export default abstract class DumpBase implements IDump {
+  data: any;
+  _files: any[];
+
+  constructor(data, files) {
+    this.data = data;
+    this._files = files;
+  }
+
+  get files(): any[] {
+    return Object.values(this._files).flat();
+  }
+
+  abstract get type(): String;
+}
+
+interface IDump {
+  files: Array<Object>;
+  type: String,
+}
\ No newline at end of file
diff --git a/tools/winscope/src/dumps/SurfaceFlinger.ts b/tools/winscope/src/dumps/SurfaceFlinger.ts
new file mode 100644
index 0000000..f354817
--- /dev/null
+++ b/tools/winscope/src/dumps/SurfaceFlinger.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
+import DumpBase from "./DumpBase";
+
+export default class SurfaceFlinger extends DumpBase {
+  sfDumpFile: any;
+  data: any;
+
+  constructor(files) {
+    const sfDumpFile = files[FILE_TYPES.SURFACE_FLINGER_DUMP];
+    super(sfDumpFile.data, files);
+    this.sfDumpFile = sfDumpFile;
+  }
+
+  get type() {
+    return DUMP_TYPES.SURFACE_FLINGER;
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/src/dumps/Wayland.ts b/tools/winscope/src/dumps/Wayland.ts
new file mode 100644
index 0000000..b038d97
--- /dev/null
+++ b/tools/winscope/src/dumps/Wayland.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
+import DumpBase from "./DumpBase";
+
+export default class WayLand extends DumpBase {
+  waylandFile: any;
+
+  constructor(files) {
+    const waylandFile = files[FILE_TYPES.WAYLAND_DUMP];
+    super(waylandFile.data, files);
+    this.waylandFile = waylandFile;
+  }
+
+  get type() {
+    return DUMP_TYPES.WAYLAND;
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/src/dumps/WindowManager.ts b/tools/winscope/src/dumps/WindowManager.ts
new file mode 100644
index 0000000..4a01ef7
--- /dev/null
+++ b/tools/winscope/src/dumps/WindowManager.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
+import DumpBase from "./DumpBase";
+
+import { WindowManagerTraceEntry } from '@/flickerlib';
+
+export default class WindowManager extends DumpBase {
+  wmDumpFile: any;
+
+  constructor(files) {
+    const wmDumpFile = files[FILE_TYPES.WINDOW_MANAGER_DUMP];
+    super(wmDumpFile.data, files);
+    this.wmDumpFile = wmDumpFile
+  }
+
+  get type() {
+    return DUMP_TYPES.WINDOW_MANAGER;
+  }
+
+  static fromProto(proto): WindowManagerTraceEntry {
+    return WindowManagerTraceEntry.fromProto(proto);
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/README.md b/tools/winscope/src/flickerlib/README.md
new file mode 100644
index 0000000..79cbbe6
--- /dev/null
+++ b/tools/winscope/src/flickerlib/README.md
@@ -0,0 +1,12 @@
+This directory contains all the code extending the common Flicker library
+to make it fully compatible with Winscope. The common Flicker library is
+written is Kotlin and compiled to JavaScript and then extended by the code in
+this directory.
+
+To use flickerlib in the rest of the Winscope source code use
+`import { ... } from '@/flickerlib'` rather than importing the compiled
+common Flicker library directly.
+
+The flickerlib classes are extended through mixins (functions, getter, and
+setters) that are injected into the original compiled common Flicker library
+classes.
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/WindowManagerTrace.ts b/tools/winscope/src/flickerlib/WindowManagerTrace.ts
new file mode 100644
index 0000000..d8f1d78
--- /dev/null
+++ b/tools/winscope/src/flickerlib/WindowManagerTrace.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+  WindowManagerTrace,
+} from "./common"
+
+import WindowManagerTraceEntry from "./WindowManagerTraceEntry"
+
+WindowManagerTrace.fromProto = function (proto) {
+  const entries = []
+  for (const entryProto of proto.entry) {
+    const transformedEntry = WindowManagerTraceEntry.fromProto(
+      entryProto.windowManagerService, entryProto.elapsedRealtimeNanos)
+    entries.push(transformedEntry)
+  }
+
+  const source = null;
+  const sourceChecksum = null;
+  return new WindowManagerTrace(entries, source, sourceChecksum)
+}
+
+export default WindowManagerTrace;
diff --git a/tools/winscope/src/flickerlib/WindowManagerTraceEntry.ts b/tools/winscope/src/flickerlib/WindowManagerTraceEntry.ts
new file mode 100644
index 0000000..efca776
--- /dev/null
+++ b/tools/winscope/src/flickerlib/WindowManagerTraceEntry.ts
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { nanosToString, TimeUnits } from "../utils/utils.js"
+
+import { WindowManagerTraceEntry } from "./common"
+
+import { applyMixins } from "./mixin"
+
+import ITreeViewElement from './treeview/IClickableTreeViewElement'
+import IClickableTreeViewElement from './treeview/IClickableTreeViewElement'
+import Chip from './treeview/Chip'
+import WindowContainer from "./windows/WindowContainer"
+
+class WindowManagerTraceEntryMixin implements IClickableTreeViewElement {
+  common: any
+  kind: string
+  name: string
+  shortName: string
+  stableId: string
+  chips: Array<Chip>
+  children: Array<ITreeViewElement>
+  obj: any
+  rawTreeViewObject
+
+  timestamp: number
+  rootWindow
+
+  mixinConstructor(obj) {
+    const name = this.timestamp ? nanosToString(this.timestamp, TimeUnits.MILLI_SECONDS) : ""
+    this.kind = "entry"
+    this.name = name
+    this.shortName = name
+    this.stableId = "entry"
+    this.children = this.rootWindow.children
+    this.chips = []
+    this.obj = obj
+  }
+
+  static fromProto(proto, timestamp) {
+    const rootWindow =
+      WindowContainer.fromProto(proto.rootWindowContainer.windowContainer, null)
+    const windowManagerTraceEntry =
+      new WindowManagerTraceEntry(rootWindow, timestamp)
+
+    windowManagerTraceEntry.kind = 'service'
+
+    windowManagerTraceEntry.focusedApp = proto.focusedApp
+    windowManagerTraceEntry.focusedDisplayId = proto.focusedDisplayId
+    windowManagerTraceEntry.lastOrientation = proto.lastOrientation
+    windowManagerTraceEntry.policy = proto.policy
+    windowManagerTraceEntry.rotation = proto.rotation
+    windowManagerTraceEntry.displayFrozen = proto.displayFrozen
+    windowManagerTraceEntry.inputMethodWindow = proto.inputMethodWindow
+
+    // Remove anything that is part of the children elements
+    // allows for faster loading of properties and less information cluttering
+    // this applied to anywhere the proto is passed to be saved as .obj
+    const obj = Object.assign({}, proto)
+    obj.rootWindowContainer = {};
+    Object.assign(obj.rootWindowContainer,
+      proto.rootWindowContainer)
+    obj.rootWindowContainer.windowContainer = {};
+    Object.assign(obj.rootWindowContainer.windowContainer,
+      proto.rootWindowContainer.windowContainer)
+    delete obj.rootWindowContainer.windowContainer.children
+    windowManagerTraceEntry.mixinConstructor(obj)
+
+    return windowManagerTraceEntry
+  }
+
+  attachObject(obj) {
+    this.obj = obj
+  }
+
+  asRawTreeViewObject() {
+    // IMPORTANT: We want to always return the same tree view object and not
+    // generate a new one every time this function is called.
+    if (!this.rawTreeViewObject) {
+      const children = this.children.map(child => child.asRawTreeViewObject())
+
+      this.rawTreeViewObject = {
+        kind: this.kind,
+        name: this.name,
+        shortName: this.shortName,
+        stableId: this.stableId,
+        chips: this.chips,
+        obj: this.obj,
+        children,
+        ref: this,
+      }
+    }
+
+    return this.rawTreeViewObject;
+  }
+}
+
+applyMixins(WindowManagerTraceEntry, [WindowManagerTraceEntryMixin])
+
+export default WindowManagerTraceEntry;
diff --git a/tools/winscope/src/flickerlib/common.js b/tools/winscope/src/flickerlib/common.js
new file mode 100644
index 0000000..44f70d1
--- /dev/null
+++ b/tools/winscope/src/flickerlib/common.js
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Imports all the compiled common Flicker library classes and exports them
+// as clean es6 modules rather than having them be commonjs modules
+
+const WindowManagerTrace = require('flicker').com.android.server.wm.flicker
+    .common.traces.windowmanager.WindowManagerTrace;
+const WindowManagerTraceEntry = require('flicker').com.android.server.wm.
+    flicker.common.traces.windowmanager.WindowManagerTraceEntry;
+
+const WindowContainer = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.WindowContainer;
+const WindowState = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.WindowState;
+const DisplayContent = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.DisplayContent;
+const ActivityRecord = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.ActivityRecord;
+const WindowToken = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.WindowToken;
+const DisplayArea = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.DisplayArea;
+const RootDisplayArea = require('flicker').com.android.server.wm.flicker.common.
+    traces.windowmanager.windows.RootDisplayArea;
+const Task = require('flicker').com.android.server.wm.flicker.common.traces.
+    windowmanager.windows.Task;
+
+const Rect = require('flicker').com.android.server.wm.flicker.common.Rect;
+const Bounds = require('flicker').com.android.server.wm.flicker.common.Bounds;
+
+export {
+  WindowManagerTrace,
+  WindowManagerTraceEntry,
+  WindowContainer,
+  WindowState,
+  DisplayContent,
+  ActivityRecord,
+  WindowToken,
+  DisplayArea,
+  RootDisplayArea,
+  Task,
+  Rect,
+  Bounds,
+};
diff --git a/tools/winscope/src/flickerlib/index.js b/tools/winscope/src/flickerlib/index.js
new file mode 100644
index 0000000..9c731b1
--- /dev/null
+++ b/tools/winscope/src/flickerlib/index.js
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import WindowManagerTraceEntry from './WindowManagerTraceEntry';
+import WindowManagerTrace from './WindowManagerTrace';
+
+/**
+ * Entry point into the flickerlib for Winscope.
+ * Expose everything we want Winscope to have access to here.
+ */
+export {WindowManagerTraceEntry, WindowManagerTrace};
+
diff --git a/tools/winscope/src/flickerlib/mixin.ts b/tools/winscope/src/flickerlib/mixin.ts
new file mode 100644
index 0000000..5e6c836
--- /dev/null
+++ b/tools/winscope/src/flickerlib/mixin.ts
@@ -0,0 +1,23 @@
+/**
+ * Injects all the properties (getters, setters, functions...) of a list of
+ * classes (baseCtors) into a class (derivedCtor).
+ * @param derivedCtor The constructor of the class we want to inject the
+ *                    properties into.
+ * @param baseCtors A list of consturctor of the classes we want to mixin the
+ *                  properties of into the derivedCtor.
+ */
+export function applyMixins(derivedCtor: any, baseCtors: any[]) {
+  baseCtors.forEach(baseCtor => {
+    Object.getOwnPropertyNames(baseCtor).forEach(name => {
+      if (['length', 'name', 'prototype'].includes(name)) {
+        return;
+      }
+
+      Object.defineProperty(derivedCtor, name, Object.getOwnPropertyDescriptor(baseCtor, name))
+    })
+
+    Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
+      Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
+    })
+  });
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/Chip.ts b/tools/winscope/src/flickerlib/treeview/Chip.ts
new file mode 100644
index 0000000..30f1c83
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/Chip.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ChipType from "./ChipType"
+
+export default class Chip {
+  short: String
+  long: String
+  type: ChipType
+
+  constructor(short: String, long: String, type: ChipType) {
+    this.short = short
+    this.long = long
+    this.type = type
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/ChipType.ts b/tools/winscope/src/flickerlib/treeview/ChipType.ts
new file mode 100644
index 0000000..70c25d7
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/ChipType.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+enum ChipType {
+  DEFAULT = 'default'
+}
+
+export default ChipType
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/Chips.ts b/tools/winscope/src/flickerlib/treeview/Chips.ts
new file mode 100644
index 0000000..8e1ebd1
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/Chips.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Chip from "./Chip"
+import ChipType from "./ChipType"
+
+export const VISIBLE_CHIP = new Chip("V", "visible", ChipType.DEFAULT)
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/IClickableTreeViewElement.ts b/tools/winscope/src/flickerlib/treeview/IClickableTreeViewElement.ts
new file mode 100644
index 0000000..7b869cd
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/IClickableTreeViewElement.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ITreeViewElement from "./ITreeViewElement"
+
+export default interface IClickableTreeViewElement extends ITreeViewElement {
+  obj: any
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/ITreeViewElement.ts b/tools/winscope/src/flickerlib/treeview/ITreeViewElement.ts
new file mode 100644
index 0000000..8e5ab74
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/ITreeViewElement.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Chip from "./Chip"
+import { TreeViewObject } from "./types"
+
+export default interface ITreeViewElement {
+  kind: String
+  name: String
+  shortName: String
+  stableId: Number | String
+  chips: Chip[]
+  children: ITreeViewElement[]
+
+  // This is used for compatibility with the "legacy" Winscope infrastructure
+  // where a class object would cause things to not work properly so this should
+  // return a raw javascript object with the relevant information.
+  // IMPORTANT: The implementation of this function should always return the
+  // same object every time it is called and not generate a new object.
+  asRawTreeViewObject(): TreeViewObject
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/treeview/types.ts b/tools/winscope/src/flickerlib/treeview/types.ts
new file mode 100644
index 0000000..1c5c942
--- /dev/null
+++ b/tools/winscope/src/flickerlib/treeview/types.ts
@@ -0,0 +1,12 @@
+import Chip from './Chip'
+
+export type TreeViewObject = {
+  kind: String
+  name: String
+  shortName: String
+  stableId: String | Number
+  chips: Chip[]
+  obj: any
+  children: TreeViewObject[]
+  ref: any
+}
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/windows/ActivityRecord.ts b/tools/winscope/src/flickerlib/windows/ActivityRecord.ts
new file mode 100644
index 0000000..cb47914
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/ActivityRecord.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+  ActivityRecord,
+} from "../common"
+
+import { applyMixins } from '../mixin'
+
+import WindowToken from "./WindowToken"
+
+export class ActivityRecordMixin {
+  get kind() {
+    return "DisplayArea"
+  }
+
+  static fromProto(proto) {
+    const windowToken = WindowToken.fromProto(proto.windowToken)
+
+    const activityRecord = new ActivityRecord(windowToken)
+
+    const obj = Object.assign({}, proto)
+    delete proto.windowToken
+    Object.assign(obj, windowToken.obj)
+    activityRecord.attachObject(obj)
+
+    return activityRecord
+  }
+}
+
+applyMixins(ActivityRecord, [ActivityRecordMixin])
+
+export default ActivityRecord
diff --git a/tools/winscope/src/flickerlib/windows/DisplayArea.ts b/tools/winscope/src/flickerlib/windows/DisplayArea.ts
new file mode 100644
index 0000000..8ff0fa2
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/DisplayArea.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+  DisplayArea,
+} from "../common"
+
+import { applyMixins } from '../mixin'
+
+import WindowContainer from "./WindowContainer"
+
+export class DisplayAreaMixin {
+  get kind() {
+    return "DisplayArea"
+  }
+
+  static fromProto(proto) {
+    const windowContainer = WindowContainer.fromProto(proto.windowContainer,
+                                                      null)
+
+    const displayArea = new DisplayArea(windowContainer)
+
+    const obj = Object.assign({}, proto)
+    delete obj.windowContainer
+    Object.assign(obj, windowContainer.obj)
+    displayArea.attachObject(obj)
+
+    return displayArea
+  }
+}
+
+applyMixins(DisplayArea, [DisplayAreaMixin])
+
+export default DisplayArea
diff --git a/tools/winscope/src/flickerlib/windows/DisplayContent.ts b/tools/winscope/src/flickerlib/windows/DisplayContent.ts
new file mode 100644
index 0000000..239cbbf
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/DisplayContent.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+  DisplayContent,
+  Bounds
+} from "../common"
+
+import { applyMixins } from '../mixin'
+
+import WindowContainer from "./WindowContainer"
+import DisplayArea from "./DisplayArea"
+
+export class DisplayContentMixin {
+  get kind() {
+    return "DisplayContent"
+  }
+
+  static fromProto(proto) {
+    let rootDisplayArea;
+    if (proto.rootDisplayArea.windowContainer == null) {
+      // For backward compatibility
+      const windowContainer = WindowContainer.fromProto(proto.windowContainer,
+                                                        null)
+      rootDisplayArea = new DisplayArea(windowContainer)
+    } else {
+      // New protos should always be using this
+      rootDisplayArea = DisplayArea.fromProto(proto.rootDisplayArea)
+    }
+
+    const bounds = new Bounds(
+      proto.displayInfo.logicalWidth || 0,
+      proto.displayInfo.logicalHeight || 0,
+    )
+
+    const displayContent = new DisplayContent(rootDisplayArea, bounds)
+
+    const obj = Object.assign({}, proto)
+    delete obj.windowContainer
+    delete obj.rootDisplayArea
+    Object.assign(obj, rootDisplayArea.obj)
+    displayContent.attachObject(obj)
+
+    return displayContent
+  }
+}
+
+applyMixins(DisplayContent, [DisplayContentMixin])
+
+export default DisplayContent
diff --git a/tools/winscope/src/flickerlib/windows/RootDisplayArea.ts b/tools/winscope/src/flickerlib/windows/RootDisplayArea.ts
new file mode 100644
index 0000000..c7acb13
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/RootDisplayArea.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { RootDisplayArea } from "../common"
+import DisplayArea from "./DisplayArea"
+
+RootDisplayArea.fromProto = DisplayArea.fromProto
+
+export default DisplayArea
\ No newline at end of file
diff --git a/tools/winscope/src/flickerlib/windows/Task.ts b/tools/winscope/src/flickerlib/windows/Task.ts
new file mode 100644
index 0000000..00b08e2
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/Task.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Task } from "../common"
+
+import { applyMixins } from '../mixin'
+
+import WindowContainer from "./WindowContainer"
+
+export class TaskMixin {
+  get kind() {
+    return "Task"
+  }
+
+  static fromProto(proto) {
+    const windowContainer = WindowContainer.fromProto(proto.windowContainer,
+                                                      null)
+
+    const task = new Task(windowContainer)
+
+    const obj = Object.assign({}, proto)
+    delete obj.windowContainer
+    Object.assign(obj, windowContainer.obj)
+    task.attachObject(obj)
+
+    return task
+  }
+}
+
+applyMixins(Task, [TaskMixin])
+
+export default Task
diff --git a/tools/winscope/src/flickerlib/windows/WindowContainer.ts b/tools/winscope/src/flickerlib/windows/WindowContainer.ts
new file mode 100644
index 0000000..d1670e5
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/WindowContainer.ts
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { WindowContainer } from "../common"
+
+import { applyMixins } from '../mixin'
+
+import IClickableTreeViewElement from '../treeview/IClickableTreeViewElement'
+import { TreeViewObject } from '../treeview/types'
+
+import DisplayArea from "./DisplayArea"
+import DisplayContent from "./DisplayContent"
+import Task from "./Task"
+import ActivityRecord from "./ActivityRecord"
+import WindowToken from "./WindowToken"
+import WindowState from "./WindowState"
+
+import { CompatibleFeatures } from '@/utils/compatibility.js'
+
+export class WindowContainerMixin implements IClickableTreeViewElement {
+  title: string
+  windowHashCode: number
+  childrenWindows
+  visible: boolean
+  rawTreeViewObject
+
+  _obj: { name: string }
+  _children
+
+  get kind() {
+    return "WindowContainer"
+  }
+
+  get name() {
+    if (this.obj && this.obj.name) {
+      return this.obj.name
+    }
+
+    if (["WindowContainer", "Task"].includes(this.title)) {
+      return null
+    }
+
+    return `${removeRedundancyInName(this.title)}@${this.windowHashCode}`
+  }
+
+  get shortName() {
+    return this.name ? shortenName(this.name) : null
+  }
+
+  get stableId() {
+    return this.windowHashCode
+  }
+
+  get children() {
+    return this._children ?? this.childrenWindows
+  }
+
+  set children(children) {
+    this._children = children
+  }
+
+  get chips() {
+    return []
+  }
+
+  set obj(obj) {
+    this._obj = obj
+  }
+
+  get obj() {
+    return this._obj
+  }
+
+  static fromProto(proto, parent_identifier) {
+    const children = proto.children.map(
+      child => transformWindowContainerChildProto(child))
+
+    // A kind of hacky way to check if the proto is from a device which
+    // didn't have the changes required for the diff viewer to work properly
+    // We required that all elements have a stable identifier in order to do the
+    // diff properly. In theory properties diff would still work, but are
+    // currently disabled. If required in the future don't hesitate to make
+    // the required changes.
+    if (!proto.identifier) {
+      CompatibleFeatures.DiffVisualization = false;
+    }
+
+    const fallback_title = parent_identifier?.title ?? ""
+    const fallback_hashCode = parent_identifier?.hashCode ?? ""
+    const title = proto.identifier?.title ?? fallback_title
+    const hashCode = proto.identifier?.hashCode ?? fallback_hashCode
+    const visible = proto.visible
+    const windowContainer =
+      new WindowContainer(children, title, hashCode, visible)
+
+    const obj = Object.assign({}, proto)
+    // we remove the children property from the object to avoid it showing the
+    // the properties view of the element as we can always see those elements'
+    // properties by changing the target element in the hierarchy tree view.
+    delete obj.children
+    windowContainer.attachObject(obj)
+
+    return windowContainer
+  }
+
+  attachObject(obj) {
+    this._obj = obj
+  }
+
+  asRawTreeViewObject(): TreeViewObject {
+    // IMPORTANT: We want to always return the same tree view object and not
+    // generate a new one every time this function is called.
+    if (!this.rawTreeViewObject) {
+      const children = this.children.map(child => child.asRawTreeViewObject())
+
+      this.rawTreeViewObject = {
+        kind: this.kind,
+        name: this.name,
+        shortName: this.shortName,
+        stableId: this.stableId,
+        chips: this.chips,
+        obj: this.obj,
+        children,
+        ref: this,
+      };
+    }
+
+    return this.rawTreeViewObject;
+  }
+}
+
+applyMixins(WindowContainer, [WindowContainerMixin])
+
+function transformWindowContainerChildProto(proto) {
+  if (proto.displayArea != null) {
+    return DisplayArea.fromProto(proto.displayArea)
+  }
+
+  if (proto.displayContent != null) {
+    return DisplayContent.fromProto(proto.displayContent)
+  }
+
+  if (proto.task != null) {
+    return Task.fromProto(proto.task)
+  }
+
+  if (proto.activity != null) {
+    return ActivityRecord.fromProto(proto.activity)
+  }
+
+  if (proto.windowToken != null) {
+    return WindowToken.fromProto(proto.windowToken)
+  }
+
+  if (proto.window != null) {
+    return WindowState.fromProto(proto.window)
+  }
+
+  throw new Error("Unhandled WindowContainerChildProto case...")
+}
+
+function removeRedundancyInName(name: string): string {
+  if (!name.includes('/')) {
+    return name
+  }
+
+  const split = name.split('/')
+  const pkg = split[0]
+  var clazz = split.slice(1).join("/")
+
+  if (clazz.startsWith("$pkg.")) {
+    clazz = clazz.slice(pkg.length + 1)
+
+    return "$pkg/$clazz"
+  }
+
+  return name
+}
+
+function shortenName(name: string): string {
+  const classParts = name.split(".")
+
+  if (classParts.length <= 3) {
+    return name
+  }
+
+  const className = classParts.slice(-1)[0] // last element
+
+  return `${classParts[0]}.${classParts[1]}.(...).${className}`
+}
+
+export default WindowContainer
diff --git a/tools/winscope/src/flickerlib/windows/WindowState.ts b/tools/winscope/src/flickerlib/windows/WindowState.ts
new file mode 100644
index 0000000..5fe547c
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/WindowState.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { WindowState, Rect } from "../common"
+import { VISIBLE_CHIP } from '../treeview/Chips'
+
+import { applyMixins } from '../mixin'
+
+import WindowContainer from "./WindowContainer"
+
+export class WindowStateMixin {
+  visible: boolean
+
+  get kind() {
+    return "WindowState"
+  }
+
+  static fromProto(proto) {
+    const windowContainer = WindowContainer.fromProto(proto.windowContainer,
+                                                      proto.identifier)
+
+    const frame = (proto.windowFrames ?? proto).frame ?? {}
+    const rect = new Rect(frame.left ?? 0, frame.top ?? 0, frame.right ?? 0, frame.bottom ?? 0)
+
+    const windowState =
+      new WindowState(windowContainer, /* childWindows */[], rect)
+
+    const obj = Object.assign({}, proto)
+    Object.assign(obj, windowContainer.obj)
+    delete obj.windowContainer
+    windowState.attachObject(obj)
+
+    return windowState
+  }
+
+  get chips() {
+    return this.visible ? [VISIBLE_CHIP] : []
+  }
+}
+
+applyMixins(WindowState, [WindowStateMixin])
+
+export default WindowState
diff --git a/tools/winscope/src/flickerlib/windows/WindowToken.ts b/tools/winscope/src/flickerlib/windows/WindowToken.ts
new file mode 100644
index 0000000..f84ce81
--- /dev/null
+++ b/tools/winscope/src/flickerlib/windows/WindowToken.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { WindowToken } from "../common"
+
+import { applyMixins } from '../mixin'
+
+import WindowContainer from "./WindowContainer"
+
+export class WindowContainerMixin {
+  get kind() {
+    return "WindowToken"
+  }
+
+  static fromProto(proto) {
+    const windowContainer = WindowContainer.fromProto(proto.windowContainer,
+                                                      null)
+
+    const windowToken = new WindowToken(windowContainer)
+
+    const obj = Object.assign({}, proto)
+    Object.assign(obj, windowContainer.obj)
+    delete obj.windowContainer
+    windowToken.attachObject(obj)
+
+    return windowToken
+  }
+}
+
+applyMixins(WindowToken, [WindowContainerMixin])
+
+export default WindowToken
diff --git a/tools/winscope/src/index_template.html b/tools/winscope/src/index_template.html
index 582fa10..a7168a1 100644
--- a/tools/winscope/src/index_template.html
+++ b/tools/winscope/src/index_template.html
@@ -13,10 +13,11 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<html lang="en">
+<html lang="en" class="md-scrollbar">
   <head>
     <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
     <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+    <link rel="icon" type="image/svg" href="static/favicon.svg"/>
 
     <meta charset="utf-8">
     <title>winscope</title>
diff --git a/tools/winscope/src/main.js b/tools/winscope/src/main.js
index 981a9fe..9c18165 100644
--- a/tools/winscope/src/main.js
+++ b/tools/winscope/src/main.js
@@ -15,13 +15,332 @@
  */
 
 import Vue from 'vue'
+import Vuex from 'vuex'
+import VueMaterial from 'vue-material'
+
 import App from './App.vue'
+import { TRACE_TYPES, DUMP_TYPES, TRACE_INFO, DUMP_INFO } from './decode.js'
+import { DIRECTION, findLastMatchingSorted, stableIdCompatibilityFixup } from './utils/utils.js'
 
 import 'style-loader!css-loader!vue-material/dist/vue-material.css'
-import VueMaterial from 'vue-material'
-Vue.use(VueMaterial);
+import 'style-loader!css-loader!vue-material/dist/theme/default.css'
+
+Vue.use(Vuex)
+Vue.use(VueMaterial)
+
+// Used to determine the order in which files or displayed
+const fileOrder = {
+  [TRACE_TYPES.WINDOW_MANAGER]: 1,
+  [TRACE_TYPES.SURFACE_FLINGER]: 2,
+  [TRACE_TYPES.TRANSACTION]: 3,
+  [TRACE_TYPES.PROTO_LOG]: 4,
+  [TRACE_TYPES.IME_CLIENTS]: 5,
+  [TRACE_TYPES.IME_SERVICE]: 6,
+  [TRACE_TYPES.IME_MANAGERSERVICE]: 7,
+};
+
+function sortFiles(files) {
+  return files.sort(
+    (a, b) => (fileOrder[a.type] ?? Infinity) - (fileOrder[b.type] ?? Infinity));
+};
+
+/**
+ * Find the smallest timeline timestamp in a list of files
+ * @return undefined if not timestamp exists in the timelines of the files
+ */
+function findSmallestTimestamp(files) {
+  let timestamp = Infinity;
+  for (const file of files) {
+    if (file.timeline[0] && file.timeline[0] < timestamp) {
+      timestamp = file.timeline[0];
+    }
+  }
+
+  return timestamp === Infinity ? undefined : timestamp;
+}
+
+const store = new Vuex.Store({
+  state: {
+    currentTimestamp: 0,
+    traces: {},
+    dumps: {},
+    excludeFromTimeline: [
+      TRACE_TYPES.PROTO_LOG,
+    ],
+    activeFile: null,
+    focusedFile: null,
+    mergedTimeline: null,
+    navigationFilesFilter: f => true,
+    // obj -> bool, identifies whether or not an item is collapsed in a treeView
+    collapsedStateStore: {},
+  },
+  getters: {
+    collapsedStateStoreFor: (state) => (item) => {
+      if (item.stableId === undefined || item.stableId === null) {
+        console.error("Missing stable ID for item", item);
+        throw new Error("Failed to get collapse state of item — missing a stableId");
+      }
+
+      return state.collapsedStateStore[stableIdCompatibilityFixup(item)];
+    },
+    files(state) {
+      return Object.values(state.traces).concat(Object.values(state.dumps));
+    },
+    sortedFiles(state, getters) {
+      return sortFiles(getters.files);
+    },
+    timelineFiles(state, getters) {
+      return Object.values(state.traces)
+        .filter(file => !state.excludeFromTimeline.includes(file.type));
+    },
+    sortedTimelineFiles(state, getters) {
+      return sortFiles(getters.timelineFiles);
+    },
+    video(state) {
+      return state.traces[TRACE_TYPES.SCREEN_RECORDING];
+    },
+  },
+  mutations: {
+    setCurrentTimestamp(state, timestamp) {
+      state.currentTimestamp = timestamp;
+    },
+    setFileEntryIndex(state, { type, entryIndex }) {
+      if (state.traces[type]) {
+        state.traces[type].selectedIndex = entryIndex;
+      } else {
+        throw new Error("Unexpected type — not a trace...");
+      }
+    },
+    setFiles(state, files) {
+      const filesByType = {};
+      for (const file of files) {
+        if (!filesByType[file.type]) {
+          filesByType[file.type] = [];
+        }
+        filesByType[file.type].push(file);
+      }
+
+      // TODO: Extract into smaller functions
+      const traces = {};
+      for (const traceType of Object.values(TRACE_TYPES)) {
+        const traceFiles = {};
+        const typeInfo = TRACE_INFO[traceType];
+
+        for (const traceDataFile of typeInfo.files) {
+
+          const files = filesByType[traceDataFile.type];
+
+          if (!files) {
+            continue;
+          }
+
+          if (traceDataFile.oneOf) {
+            if (files.length > 1) {
+              throw new Error(`More than one file of type ${traceDataFile.type} has been provided`);
+            }
+
+            traceFiles[traceDataFile.type] = files[0];
+          } else if (traceDataFile.manyOf) {
+            traceFiles[traceDataFile.type] = files;
+          } else {
+            throw new Error("Missing oneOf or manyOf property...");
+          }
+        }
+
+        if (Object.keys(traceFiles).length > 0 && typeInfo.constructor) {
+          traces[traceType] = new typeInfo.constructor(traceFiles);
+        }
+      }
+
+      state.traces = traces;
+
+      // TODO: Refactor common code out
+      const dumps = {};
+      for (const dumpType of Object.values(DUMP_TYPES)) {
+        const dumpFiles = {};
+        const typeInfo = DUMP_INFO[dumpType];
+
+        for (const dumpDataFile of typeInfo.files) {
+          const files = filesByType[dumpDataFile.type];
+
+          if (!files) {
+            continue;
+          }
+
+          if (dumpDataFile.oneOf) {
+            if (files.length > 1) {
+              throw new Error(`More than one file of type ${dumpDataFile.type} has been provided`);
+            }
+
+            dumpFiles[dumpDataFile.type] = files[0];
+          } else if (dumpDataFile.manyOf) {
+
+          } else {
+            throw new Error("Missing oneOf or manyOf property...");
+          }
+        }
+
+        if (Object.keys(dumpFiles).length > 0 && typeInfo.constructor) {
+          dumps[dumpType] = new typeInfo.constructor(dumpFiles);
+        }
+
+      }
+
+      state.dumps = dumps;
+
+      if (!state.activeFile && Object.keys(traces).length > 0) {
+        state.activeFile = sortFiles(Object.values(traces))[0];
+      }
+
+      // TODO: Add same for dumps
+    },
+    clearFiles(state) {
+      for (const traceType in state.traces) {
+        if (state.traces.hasOwnProperty(traceType)) {
+          Vue.delete(state.traces, traceType);
+        }
+      }
+
+      for (const dumpType in state.dumps) {
+        if (state.dumps.hasOwnProperty(dumpType)) {
+          Vue.delete(state.dumps, dumpType);
+        }
+      }
+
+      state.activeFile = null;
+      state.mergedTimeline = null;
+    },
+    setActiveFile(state, file) {
+      state.activeFile = file;
+    },
+    setMergedTimeline(state, timeline) {
+      state.mergedTimeline = timeline;
+    },
+    removeMergedTimeline(state, timeline) {
+      state.mergedTimeline = null;
+    },
+    setMergedTimelineIndex(state, newIndex) {
+      state.mergedTimeline.selectedIndex = newIndex;
+    },
+    setCollapsedState(state, { item, isCollapsed }) {
+      if (item.stableId === undefined || item.stableId === null) {
+        return;
+      }
+
+      Vue.set(
+        state.collapsedStateStore,
+        stableIdCompatibilityFixup(item),
+        isCollapsed
+      );
+    },
+    setFocusedFile(state, file) {
+      state.focusedFile = file;
+    },
+    setNavigationFilesFilter(state, filter) {
+      state.navigationFilesFilter = filter;
+    },
+  },
+  actions: {
+    setFiles(context, files) {
+      context.commit('clearFiles');
+      context.commit('setFiles', files);
+
+      const timestamp = findSmallestTimestamp(files);
+      if (timestamp !== undefined) {
+        context.commit('setCurrentTimestamp', timestamp);
+      }
+    },
+    updateTimelineTime(context, timestamp) {
+      for (const file of context.getters.files) {
+        const type = file.type;
+        const entryIndex = findLastMatchingSorted(
+          file.timeline,
+          (array, idx) => parseInt(array[idx]) <= timestamp,
+        );
+
+        context.commit('setFileEntryIndex', { type, entryIndex });
+      }
+
+      if (context.state.mergedTimeline) {
+        const newIndex = findLastMatchingSorted(
+          context.state.mergedTimeline.timeline,
+          (array, idx) => parseInt(array[idx]) <= timestamp,
+        );
+
+        context.commit('setMergedTimelineIndex', newIndex);
+      }
+
+      context.commit('setCurrentTimestamp', timestamp);
+    },
+    advanceTimeline(context, direction) {
+      // NOTE: MergedTimeline is never considered to find the next closest index
+      // MergedTimeline only represented the timelines overlapped together and
+      // isn't considered an actual timeline.
+
+      if (direction !== DIRECTION.FORWARD && direction !== DIRECTION.BACKWARD) {
+        throw new Error("Unsupported direction provided.");
+      }
+
+      const consideredFiles = context.getters.timelineFiles
+        .filter(context.state.navigationFilesFilter);
+
+      let fileIndex = -1;
+      let timelineIndex;
+      let minTimeDiff = Infinity;
+
+      for (let idx = 0; idx < consideredFiles.length; idx++) {
+        const file = consideredFiles[idx];
+
+        let candidateTimestampIndex = file.selectedIndex;
+        let candidateTimestamp = file.timeline[candidateTimestampIndex];
+
+        let candidateCondition;
+        switch (direction) {
+          case DIRECTION.BACKWARD:
+            candidateCondition = () => candidateTimestamp < context.state.currentTimestamp;
+            break;
+          case DIRECTION.FORWARD:
+            candidateCondition = () => candidateTimestamp > context.state.currentTimestamp;
+            break;
+        }
+
+        if (!candidateCondition()) {
+          // Not a candidate — find a valid candidate
+          let noCandidate = false;
+          while (!candidateCondition()) {
+            candidateTimestampIndex += direction;
+            if (candidateTimestampIndex < 0 || candidateTimestampIndex >= file.timeline.length) {
+              noCandidate = true;
+              break;
+            }
+            candidateTimestamp = file.timeline[candidateTimestampIndex];
+          }
+
+          if (noCandidate) {
+            continue;
+          }
+        }
+
+        const timeDiff = Math.abs(candidateTimestamp - context.state.currentTimestamp);
+        if (minTimeDiff > timeDiff) {
+          minTimeDiff = timeDiff;
+          fileIndex = idx;
+          timelineIndex = candidateTimestampIndex;
+        }
+      }
+
+      if (fileIndex >= 0) {
+        const closestFile = consideredFiles[fileIndex];
+        const timestamp = parseInt(closestFile.timeline[timelineIndex]);
+
+        context.dispatch('updateTimelineTime', timestamp);
+      }
+    }
+  }
+})
 
 new Vue({
   el: '#app',
+  store, // inject the Vuex store into all components
   render: h => h(App)
 })
diff --git a/tools/winscope/src/mixins/FileType.js b/tools/winscope/src/mixins/FileType.js
new file mode 100644
index 0000000..b2541b9
--- /dev/null
+++ b/tools/winscope/src/mixins/FileType.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TRACE_TYPES, DUMP_TYPES} from '@/decode.js';
+
+const mixin = {
+  showInTraceView(file) {
+    return file.type == TRACE_TYPES.WINDOW_MANAGER ||
+      file.type == TRACE_TYPES.SURFACE_FLINGER ||
+      file.type == TRACE_TYPES.WAYLAND ||
+      file.type == TRACE_TYPES.SYSTEM_UI ||
+      file.type == TRACE_TYPES.LAUNCHER ||
+      file.type == TRACE_TYPES.IME_CLIENTS ||
+      file.type == TRACE_TYPES.IME_SERVICE ||
+      file.type == TRACE_TYPES.IME_MANAGERSERVICE ||
+      file.type == DUMP_TYPES.WINDOW_MANAGER ||
+      file.type == DUMP_TYPES.SURFACE_FLINGER ||
+      file.type == DUMP_TYPES.WAYLAND;
+  },
+  showInWindowManagerTraceView(file) {
+    return file.type == TRACE_TYPES.WINDOW_MANAGER ||
+        file.type == DUMP_TYPES.WINDOW_MANAGER;
+  },
+  showInSurfaceFlingerTraceView(file) {
+    return file.type == TRACE_TYPES.SURFACE_FLINGER ||
+        file.type == DUMP_TYPES.SURFACE_FLINGER;
+  },
+  isVideo(file) {
+    return file.type == TRACE_TYPES.SCREEN_RECORDING;
+  },
+  isTransactions(file) {
+    return file.type == TRACE_TYPES.TRANSACTION;
+  },
+  isLog(file) {
+    return file.type == TRACE_TYPES.PROTO_LOG;
+  },
+  hasDataView(file) {
+    return this.isLog(file) || this.showInTraceView(file) ||
+      this.isTransactions(file);
+  },
+};
+
+export {mixin};
+
+export default {
+  name: 'FileType',
+  methods: mixin,
+};
diff --git a/tools/winscope/src/mixins/FocusedDataViewFinder.js b/tools/winscope/src/mixins/FocusedDataViewFinder.js
new file mode 100644
index 0000000..27f341f
--- /dev/null
+++ b/tools/winscope/src/mixins/FocusedDataViewFinder.js
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export default {
+  name: 'FocusedDataViewFinder',
+  created() {
+    document.addEventListener('scroll', this.updateFocusedView);
+  },
+  deleted() {
+    document.removeEventListener('scroll', this.updateFocusedView);
+  },
+  computed: {
+    timelineFiles() {
+      return this.$store.getters.timelineFiles;
+    },
+  },
+  methods: {
+    updateFocusedView() {
+      const positions = this.getDataViewPositions();
+      const focusedFile = this.findFocusedDataView(positions);
+
+      this.$store.commit('setFocusedFile', focusedFile);
+    },
+    getDataViewPositions() {
+      const positions = {};
+
+      for (const file of this.files) {
+        const dataView = this.$refs[file.type];
+        if (!dataView || dataView.length === 0) {
+          continue;
+        }
+
+        const dataViewEl = dataView[0].$el;
+        positions[file.type] = dataViewEl.getBoundingClientRect();
+      }
+
+      return positions;
+    },
+    /**
+     * Returns the file of the DataView that takes up the most of the visible
+     * screen space.
+     * @param {Object} positions A map from filenames to their respective
+     *                           boundingClientRect.
+     * @return {String} The dataView that is in focus.
+     */
+    findFocusedDataView(positions) {
+      const visibleHeight =
+        Math.max(document.documentElement.clientHeight || 0,
+            window.innerHeight || 0);
+
+      let maxScreenSpace = 0;
+      let focusedDataView = this.files[0];
+      for (const file of this.files) {
+        const pos = positions[file.type];
+        if (!pos) {
+          continue;
+        }
+
+        let screenSpace = 0;
+        if (0 <= pos.top && pos.top <= visibleHeight) {
+          screenSpace = Math.min(visibleHeight, pos.bottom) - pos.top;
+        } else if (0 <= pos.bottom && pos.bottom <= visibleHeight) {
+          screenSpace = pos.bottom - Math.max(0, pos.top);
+        } else if (pos.top <= 0 && pos.bottom >= visibleHeight) {
+          screenSpace = visibleHeight;
+        }
+
+        if (screenSpace >= maxScreenSpace) {
+          maxScreenSpace = screenSpace;
+          focusedDataView = file;
+        }
+      }
+
+      return focusedDataView;
+    },
+  },
+};
diff --git a/tools/winscope/src/mixins/SaveAsZip.js b/tools/winscope/src/mixins/SaveAsZip.js
new file mode 100644
index 0000000..8b8b6e1
--- /dev/null
+++ b/tools/winscope/src/mixins/SaveAsZip.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import JSZip from 'jszip';
+
+export default {
+  name: 'SaveAsZip',
+  methods: {
+    saveAs(blob, filename) {
+      const a = document.createElement('a');
+      a.style = 'display: none';
+      document.body.appendChild(a);
+
+      const url = window.URL.createObjectURL(blob);
+
+      a.href = url;
+      a.download = filename;
+      a.click();
+      window.URL.revokeObjectURL(url);
+
+      document.body.removeChild(a);
+    },
+    async downloadAsZip(traces) {
+      const zip = new JSZip();
+
+      for (const trace of traces) {
+        const traceFolder = zip.folder(trace.type);
+        for (const file of trace.files) {
+          const blob = await fetch(file.blobUrl).then((r) => r.blob());
+          traceFolder.file(file.filename, blob);
+        }
+      }
+
+      const zipFile = await zip.generateAsync({type: 'blob'});
+
+      this.saveAs(zipFile, 'winscope.zip');
+    },
+  },
+};
diff --git a/tools/winscope/src/mixins/Timeline.js b/tools/winscope/src/mixins/Timeline.js
new file mode 100644
index 0000000..1f94e3a
--- /dev/null
+++ b/tools/winscope/src/mixins/Timeline.js
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Represents a continuous section of the timeline that is rendered into the
+ * timeline svg.
+ */
+class Block {
+  /**
+   * Create a block.
+   * @param {number} startPos - The start position of the block as a percentage
+   * of the timeline width.
+   * @param {number} width - The width of the block as a percentage of the
+   * timeline width.
+   */
+  constructor(startPos, width) {
+    this.startPos = startPos;
+    this.width = width;
+  }
+}
+
+/**
+ * This Mixin should only be injected into components which have the following:
+ * - An element in the template referenced as 'timeline' (this.$refs.timeline).
+ */
+export default {
+  name: 'timeline',
+  props: {
+    /**
+     * A 'timeline' as an array of timestamps
+     */
+    'timeline': {
+      type: Array,
+    },
+    /**
+     * A scale factor is an array of two elements, the min and max timestamps of
+     * the timeline
+     */
+    'scale': {
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      /**
+       * Is a number representing the percentage of the timeline a block should
+       * be at a minimum or what percentage of the timeline a single entry takes
+       * up when rendered.
+       */
+      pointWidth: 1,
+    };
+  },
+  computed: {
+    /**
+     * Converts the timeline (list of timestamps) to an array of blocks to be
+     * displayed. This is to have fewer elements in the rendered timeline.
+     * Instead of having one rect for each timestamp in the timeline we only
+     * have one for each continuous segment of the timeline. This is to improve
+     * both the Vue patching step's performance and the DOM rendering
+     * performance.
+     */
+    timelineBlocks() {
+      const blocks = [];
+
+      // The difference in time between two timestamps after which they are no
+      // longer rendered as a continuous segment/block.
+      const overlapDistanceInTs = (this.scale[1] - this.scale[0]) *
+        ((this.crop?.right ?? 1) - (this.crop?.left ?? 0)) *
+        1 / (100 - this.pointWidth);
+
+      let blockStartTs = this.timeline[0];
+      for (let i = 1; i < this.timeline.length; i++) {
+        const lastTs = this.timeline[i - 1];
+        const ts = this.timeline[i];
+        if (ts - lastTs > overlapDistanceInTs) {
+          const block = this.generateTimelineBlock(blockStartTs, lastTs);
+          blocks.push(block);
+          blockStartTs = ts;
+        }
+      }
+
+      const blockEndTs = this.timeline[this.timeline.length - 1];
+      const block = this.generateTimelineBlock(blockStartTs, blockEndTs);
+      blocks.push(block);
+
+      return Object.freeze(blocks);
+    },
+  },
+  methods: {
+    position(item) {
+      let pos;
+      pos = this.translate(item);
+      pos = this.applyCrop(pos);
+
+      return pos * (100 - this.pointWidth);
+    },
+
+    translate(cx) {
+      const scale = [...this.scale];
+      if (scale[0] >= scale[1]) {
+        return cx;
+      }
+
+      return (cx - scale[0]) / (scale[1] - scale[0]);
+    },
+
+    untranslate(pos) {
+      const scale = [...this.scale];
+      if (scale[0] >= scale[1]) {
+        return pos;
+      }
+
+      return pos * (scale[1] - scale[0]) + scale[0];
+    },
+
+    applyCrop(cx) {
+      if (!this.crop) {
+        return cx;
+      }
+
+      return (cx - this.crop.left) / (this.crop.right - this.crop.left);
+    },
+
+    unapplyCrop(pos) {
+      if (!this.crop) {
+        return pos;
+      }
+
+      return pos * (this.crop.right - this.crop.left) + this.crop.left;
+    },
+
+    /**
+     * Converts a position as a percentage of the timeline width to a timestamp.
+     * @param {number} position - target position as a percentage of the
+     *                            timeline's width.
+     * @return {number} The index of the closest timestamp in the timeline to
+     *                  the target position.
+     */
+    positionToTsIndex(position) {
+      let targetTimestamp = position / (100 - this.pointWidth);
+      targetTimestamp = this.unapplyCrop(targetTimestamp);
+      targetTimestamp = this.untranslate(targetTimestamp);
+
+      // The index of the timestamp in the timeline that is closest to the
+      // targetTimestamp.
+      const closestTsIndex = this.findClosestTimestampIndexTo(targetTimestamp);
+
+      return closestTsIndex;
+    },
+
+    indexOfClosestElementTo(target, array) {
+      let smallestDiff = Math.abs(target - array[0]);
+      let closestIndex = 0;
+      for (let i = 1; i < array.length; i++) {
+        const elem = array[i];
+        if (Math.abs(target - elem) < smallestDiff) {
+          closestIndex = i;
+          smallestDiff = Math.abs(target - elem);
+        }
+      }
+
+      return closestIndex;
+    },
+
+    findClosestTimestampIndexTo(ts) {
+      let left = 0;
+      let right = this.timeline.length - 1;
+      let mid = Math.floor((left + right) / 2);
+
+      while (left < right) {
+        if (ts < this.timeline[mid]) {
+          right = mid - 1;
+        } else if (ts > this.timeline[mid]) {
+          left = mid + 1;
+        } else {
+          return mid;
+        }
+        mid = Math.floor((left + right) / 2);
+      }
+
+      const candidateElements = this.timeline.slice(left - 1, right + 2);
+      const closestIndex =
+        this.indexOfClosestElementTo(ts, candidateElements) + (left - 1);
+      return closestIndex;
+    },
+
+    /**
+     * Transforms an absolute position in the timeline to a timestamp present in
+     * the timeline.
+     * @param {number} absolutePosition - Pixels from the left of the timeline.
+     * @return {number} The timestamp in the timeline that is closest to the
+     *                  target position.
+     */
+    absolutePositionAsTimestamp(absolutePosition) {
+      const timelineWidth = this.$refs.timeline.clientWidth;
+      const position = (absolutePosition / timelineWidth) * 100;
+
+      return this.timeline[this.positionToTsIndex(position)];
+    },
+
+    /**
+     * Handles the block click event.
+     * When a block in the timeline is clicked this function will determine
+     * the target timeline index and update the timeline to match this index.
+     * @param {MouseEvent} e - The mouse event of the click on a timeline block.
+     */
+    onBlockClick(e) {
+      const clickOffset = e.offsetX;
+      const timelineWidth = this.$refs.timeline.clientWidth;
+      const clickOffsetAsPercentage = (clickOffset / timelineWidth) * 100;
+
+      const clickedOnTsIndex =
+        this.positionToTsIndex(clickOffsetAsPercentage - this.pointWidth / 2);
+
+      if (this.disabled) {
+        return;
+      }
+      const timestamp = parseInt(this.timeline[clickedOnTsIndex]);
+      this.$store.dispatch('updateTimelineTime', timestamp);
+    },
+
+    /**
+     * Generate a block object that can be used by the timeline SVG to render
+     * a transformed block that starts at `startTs` and ends at `endTs`.
+     * @param {number} startTs - The timestamp at which the block starts.
+     * @param {number} endTs - The timestamp at which the block ends.
+     * @return {Block} A block object transformed to the timeline's crop and
+     *                 scale parameter.
+     */
+    generateTimelineBlock(startTs, endTs) {
+      const blockWidth = this.position(endTs) - this.position(startTs) +
+        this.pointWidth;
+      return Object.freeze(new Block(this.position(startTs), blockWidth));
+    },
+  },
+};
diff --git a/tools/winscope/src/sf_visibility.js b/tools/winscope/src/sf_visibility.js
index 3c70e95..11ff0cd 100644
--- a/tools/winscope/src/sf_visibility.js
+++ b/tools/winscope/src/sf_visibility.js
@@ -20,7 +20,7 @@
  * composition state (visibleRegion), it will be used otherwise it will be
  * derived.
  */
-import { multiply_rect, is_simple_rotation } from './matrix_utils.js'
+import {multiply_rect, is_simple_rotation} from './matrix_utils.js';
 
 // Layer flags
 const FLAG_HIDDEN = 0x01;
@@ -29,25 +29,27 @@
 
 function flags_to_string(flags) {
   if (!flags) return '';
-  var verboseFlags = [];
-  if (flags & FLAG_HIDDEN) verboseFlags.push("HIDDEN");
-  if (flags & FLAG_OPAQUE) verboseFlags.push("OPAQUE");
-  if (flags & FLAG_SECURE) verboseFlags.push("SECURE");
-  return verboseFlags.join('|') + " (" + flags + ")";
+  const verboseFlags = [];
+  if (flags & FLAG_HIDDEN) verboseFlags.push('HIDDEN');
+  if (flags & FLAG_OPAQUE) verboseFlags.push('OPAQUE');
+  if (flags & FLAG_SECURE) verboseFlags.push('SECURE');
+  return verboseFlags.join('|') + ' (' + flags + ')';
 }
 
 function is_empty(region) {
   return region == undefined ||
       region.rect == undefined ||
       region.rect.length == 0 ||
-      region.rect.every(function(r) { return is_empty_rect(r) } );
+      region.rect.every(function(r) {
+        return is_empty_rect(r);
+      } );
 }
 
 function is_empty_rect(rect) {
-  var right = rect.right || 0;
-  var left = rect.left || 0;
-  var top = rect.top || 0;
-  var bottom = rect.bottom || 0;
+  const right = rect.right || 0;
+  const left = rect.left || 0;
+  const top = rect.top || 0;
+  const bottom = rect.bottom || 0;
 
   return (right - left) <= 0 || (bottom - top) <= 0;
 }
@@ -73,7 +75,7 @@
  */
 function is_transform_invalid(transform) {
   return !transform || (transform.dsdx * transform.dtdy ===
-      transform.dtdx * transform.dsdy); //determinant of transform
+      transform.dtdx * transform.dsdy); // determinant of transform
 }
 
 function is_opaque(layer) {
@@ -101,21 +103,20 @@
 
   // Support newer effect layer
   return layer.type === 'EffectLayer' &&
-      (fills_color(layer) || draws_shadows(layer) || has_blur(layer))
+      (fills_color(layer) || draws_shadows(layer) || has_blur(layer));
 }
 
 function is_hidden_by_policy(layer) {
-  return layer.flags & FLAG_HIDDEN == FLAG_HIDDEN || 
+  return layer.flags & FLAG_HIDDEN == FLAG_HIDDEN ||
     // offscreen layer root has a unique layer id
     layer.id == 0x7FFFFFFD;
 }
 
 /**
  * Checks if the layer is visible based on its visibleRegion if available
- * or its type, active buffer content, alpha and properties. 
+ * or its type, active buffer content, alpha and properties.
  */
 function is_visible(layer, hiddenByPolicy, includesCompositionState) {
-
   if (includesCompositionState) {
     return !is_empty(layer.visibleRegion);
   }
@@ -131,7 +132,7 @@
   if (!layer.color || !layer.color.a || layer.color.a == 0) {
     return false;
   }
-  
+
   if (layer.occludedBy && layer.occludedBy.length > 0) {
     return false;
   }
@@ -156,7 +157,8 @@
     return 'Hidden by parent';
   }
 
-  let isBufferLayer = (layer.type === 'BufferStateLayer' || layer.type === 'BufferQueueLayer');
+  const isBufferLayer = (layer.type === 'BufferStateLayer' ||
+      layer.type === 'BufferQueueLayer');
   if (isBufferLayer && (!layer.activeBuffer ||
     layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
     return 'Buffer is empty';
@@ -170,7 +172,7 @@
     return 'Crop is 0x0';
   }
 
-  if (!layer.bounds || is_empty_rect(layer.bounds)) {  
+  if (!layer.bounds || is_empty_rect(layer.bounds)) {
     return 'Bounds is 0x0';
   }
 
@@ -181,17 +183,18 @@
     return 'RelativeOf layer has been removed';
   }
 
-  let isEffectLayer = (layer.type === 'EffectLayer');
-  if (isEffectLayer && !fills_color(layer) && !draws_shadows(layer) && !has_blur(layer)) {
+  const isEffectLayer = (layer.type === 'EffectLayer');
+  if (isEffectLayer && !fills_color(layer) &&
+      !draws_shadows(layer) && !has_blur(layer)) {
     return 'Effect layer does not have color fill, shadow or blur';
   }
-  
+
   if (layer.occludedBy && layer.occludedBy.length > 0) {
     return 'Layer is occluded by:' + layer.occludedBy.join();
-  } 
+  }
 
   if (layer.visible) {
-    return "Unknown";
+    return 'Unknown';
   };
 }
 
@@ -203,7 +206,8 @@
 
 // Returns true if outer rect contains inner rect
 function contains(outerLayer, innerLayer) {
-  if (!is_simple_rotation(outerLayer.transform) || !is_simple_rotation(innerLayer.transform)) {
+  if (!is_simple_rotation(outerLayer.transform) ||
+      !is_simple_rotation(innerLayer.transform)) {
     return false;
   }
   const outer = screen_bounds(outerLayer);
@@ -214,67 +218,88 @@
 
 function screen_bounds(layer) {
   if (layer.screenBounds) return layer.screenBounds;
-  let transformMatrix = layer.transform;
-  var tx = layer.position ? layer.position.x || 0 : 0;
-  var ty = layer.position ? layer.position.y || 0 : 0;
+  const transformMatrix = layer.transform;
+  const tx = layer.position ? layer.position.x || 0 : 0;
+  const ty = layer.position ? layer.position.y || 0 : 0;
 
-  transformMatrix.tx = tx
-  transformMatrix.ty = ty
+  transformMatrix.tx = tx;
+  transformMatrix.ty = ty;
   return multiply_rect(transformMatrix, layer.bounds);
 }
 
 // Traverse in z-order from top to bottom and fill in occlusion data
 function fill_occlusion_state(layerMap, rootLayers, includesCompositionState) {
-  const layers = rootLayers.filter(layer => !layer.isRelativeOf);
-  traverse_top_to_bottom(layerMap, layers, {opaqueRects:[], transparentRects:[], screenBounds:null}, (layer, globalState) => {
-
-    if (layer.name.startsWith("Root#0") && layer.sourceBounds) {
-      globalState.screenBounds = {left:0,  top:0, bottom:layer.sourceBounds.bottom, right:layer.sourceBounds.right};
+  const layers = rootLayers.filter((layer) => !layer.isRelativeOf);
+  traverse_top_to_bottom(layerMap, layers, {opaqueRects: [], transparentRects: [], screenBounds: null}, (layer, globalState) => {
+    if (layer.name.startsWith('Root#0') && layer.sourceBounds) {
+      globalState.screenBounds = {left: 0, top: 0, bottom: layer.sourceBounds.bottom, right: layer.sourceBounds.right};
     }
-  
+
     const visible = is_visible(layer, layer.hidden, includesCompositionState);
     if (visible) {
-      let fullyOccludes = (testLayer) => contains(testLayer, layer);
-      let partiallyOccludes = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
-      let covers = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
-  
-      layer.occludedBy = globalState.opaqueRects.filter(fullyOccludes).map(layer => layer.id);
-      layer.partiallyOccludedBy = globalState.opaqueRects.filter(partiallyOccludes).map(layer => layer.id);
-      layer.coveredBy = globalState.transparentRects.filter(covers).map(layer => layer.id);
+      const fullyOccludes = (testLayer) => contains(testLayer, layer);
+      const partiallyOccludes = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
+      const covers = (testLayer) => overlaps(screen_bounds(testLayer), screen_bounds(layer));
+
+      layer.occludedBy = globalState.opaqueRects.filter(fullyOccludes).map((layer) => layer.id);
+      layer.partiallyOccludedBy = globalState.opaqueRects.filter(partiallyOccludes).map((layer) => layer.id);
+      layer.coveredBy = globalState.transparentRects.filter(covers).map((layer) => layer.id);
 
       if (is_opaque(layer)) {
         globalState.opaqueRects.push(layer);
       } else {
-          globalState.transparentRects.push(layer);
+        globalState.transparentRects.push(layer);
       }
     }
 
     layer.visible = is_visible(layer, layer.hidden, includesCompositionState);
     if (!layer.visible) {
       layer.invisibleDueTo = get_visibility_reason(layer);
-    } 
+    }
   });
 }
 
 function traverse_top_to_bottom(layerMap, rootLayers, globalState, fn) {
-  for (var i = rootLayers.length-1; i >=0; i--) {   
-    const relatives = rootLayers[i].relatives.map(id => layerMap[id]);
-    const children = rootLayers[i].children.map(id => layerMap[id])
+  for (let i = rootLayers.length-1; i >=0; i--) {
+    const relatives = [];
+    for (const id of rootLayers[i].relatives) {
+      if (!layerMap.hasOwnProperty(id)) {
+        // TODO (b/162500053): so that this doesn't need to be checked here
+        console.warn(
+            `Relative layer with id ${id} not found in dumped layers... ` +
+            `Skipping layer in traversal...`);
+      } else {
+        relatives.push(layerMap[id]);
+      }
+    }
+
+    const children = [];
+    for (const id of rootLayers[i].children) {
+      if (!layerMap.hasOwnProperty(id)) {
+        // TODO (b/162500053): so that this doesn't need to be checked here
+        console.warn(
+            `Children layer with id ${id} not found in dumped layers... ` +
+            `Skipping layer in traversal...`);
+      } else {
+        children.push(layerMap[id]);
+      }
+    }
 
     // traverse through relatives and children that are not relatives
-    const traverseList = relatives.concat(children.filter(layer => !layer.isRelativeOf));
+    const traverseList = relatives
+        .concat(children.filter((layer) => !layer.isRelativeOf));
+
     traverseList.sort((lhs, rhs) => rhs.z - lhs.z);
-    
-    traverseList.filter((layer) => layer.z >=0).forEach(layer => {
+
+    traverseList.filter((layer) => layer.z >=0).forEach((layer) => {
       traverse_top_to_bottom(layerMap, [layer], globalState, fn);
     });
 
     fn(rootLayers[i], globalState);
-    
-    traverseList.filter((layer) => layer.z < 0).forEach(layer => {
+
+    traverseList.filter((layer) => layer.z < 0).forEach((layer) => {
       traverse_top_to_bottom(layerMap, [layer], globalState, fn);
     });
-
   }
 }
 
@@ -291,20 +316,20 @@
       } else if (parent) {
         layer.bounds = parent.bounds;
       } else {
-        layer.bounds = {left:0, top:0, right:0, bottom:0};
+        layer.bounds = {left: 0, top: 0, right: 0, bottom: 0};
       }
     }
   });
-} 
+}
 
 function traverse(layerMap, rootLayers, fn) {
-  for (var i = rootLayers.length-1; i >=0; i--) {
+  for (let i = rootLayers.length-1; i >=0; i--) {
     const parentId = rootLayers[i].parent;
     const parent = parentId == -1 ? null : layerMap[parentId];
     fn(rootLayers[i], parent);
-    const children = rootLayers[i].children.map(id => layerMap[id]);
+    const children = rootLayers[i].children.map((id) => layerMap[id]);
     traverse(layerMap, children, fn);
   }
 }
 
-export {fill_occlusion_state, fill_inherited_state};
\ No newline at end of file
+export {fill_occlusion_state, fill_inherited_state};
diff --git a/tools/winscope/src/traces/InputMethodClients.ts b/tools/winscope/src/traces/InputMethodClients.ts
new file mode 100644
index 0000000..95e1d8e
--- /dev/null
+++ b/tools/winscope/src/traces/InputMethodClients.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class InputMethodClients extends TraceBase {
+  imeTraceFileClients: any;
+
+  constructor(files) {
+    const imeTraceFileClients = files[FILE_TYPES.IME_TRACE_CLIENTS];
+    super(imeTraceFileClients.data, imeTraceFileClients.timeline, files);
+
+    this.imeTraceFileClients = imeTraceFileClients;
+  }
+
+  get type() {
+    return TRACE_TYPES.IME_CLIENTS;
+  }
+}
diff --git a/tools/winscope/src/traces/InputMethodManagerService.ts b/tools/winscope/src/traces/InputMethodManagerService.ts
new file mode 100644
index 0000000..7ae0434
--- /dev/null
+++ b/tools/winscope/src/traces/InputMethodManagerService.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class InputMethodManagerService extends TraceBase {
+  imeTraceFileManagerService: any;
+
+  constructor(files) {
+    const imeTraceFileManagerService = files[FILE_TYPES.IME_TRACE_MANAGERSERVICE];
+    super(imeTraceFileManagerService.data, imeTraceFileManagerService.timeline, files);
+
+    this.imeTraceFileManagerService = imeTraceFileManagerService;
+  }
+
+  get type() {
+    return TRACE_TYPES.IME_MANAGERSERVICE;
+  }
+}
diff --git a/tools/winscope/src/traces/InputMethodService.ts b/tools/winscope/src/traces/InputMethodService.ts
new file mode 100644
index 0000000..64b7841
--- /dev/null
+++ b/tools/winscope/src/traces/InputMethodService.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class InputMethodService extends TraceBase {
+  imeTraceFileService: any;
+
+  constructor(files) {
+    const imeTraceFileService = files[FILE_TYPES.IME_TRACE_SERVICE];
+    super(imeTraceFileService.data, imeTraceFileService.timeline, files);
+
+    this.imeTraceFileService = imeTraceFileService;
+  }
+
+  get type() {
+    return TRACE_TYPES.IME_SERVICE;
+  }
+}
diff --git a/tools/winscope/src/traces/Launcher.ts b/tools/winscope/src/traces/Launcher.ts
new file mode 100644
index 0000000..9649296
--- /dev/null
+++ b/tools/winscope/src/traces/Launcher.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class Launcher extends TraceBase {
+  launcherFile: any;
+
+  constructor(files) {
+    const launcherFile = files[FILE_TYPES.LAUNCHER];
+    super(launcherFile.data, launcherFile.timeline, files);
+
+    this.launcherFile = launcherFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.LAUNCHER;
+  }
+}
diff --git a/tools/winscope/src/traces/ProtoLog.ts b/tools/winscope/src/traces/ProtoLog.ts
new file mode 100644
index 0000000..f251114
--- /dev/null
+++ b/tools/winscope/src/traces/ProtoLog.ts
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+import { nanos_to_string } from '@/transform.js';
+import viewerConfig from
+  '@/../../../../frameworks/base/data/etc/services.core.protolog.json';
+
+export default class ProtoLog extends TraceBase {
+  protoLogFile: any;
+
+  constructor(files) {
+    const protoLogFile = files[FILE_TYPES.PROTO_LOG];
+    super(protoLogFile.data, protoLogFile.timeline, files);
+
+    this.protoLogFile = protoLogFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.PROTO_LOG;
+  }
+}
+
+export class LogMessage {
+  text: String;
+  time: String;
+  tag: String;
+  level: String;
+  at: String;
+  timestamp: Number;
+
+  constructor({ text, time, tag, level, at, timestamp }) {
+    this.text = text;
+    this.time = time;
+    this.tag = tag;
+    this.level = level;
+    this.at = at;
+    this.timestamp = timestamp;
+  }
+}
+
+export class FormattedLogMessage extends LogMessage {
+  constructor(entry) {
+    super({
+      text: (entry.messageHash.toString() +
+        ' - [' + entry.strParams.toString() +
+        '] [' + entry.sint64Params.toString() +
+        '] [' + entry.doubleParams.toString() +
+        '] [' + entry.booleanParams.toString() + ']'),
+      time: nanos_to_string(entry.elapsedRealtimeNanos),
+      tag: 'INVALID',
+      level: 'invalid',
+      at: '',
+      timestamp: entry.elapsedRealtimeNanos,
+    });
+  }
+}
+
+export class UnformattedLogMessage extends LogMessage {
+  constructor(entry, message) {
+    super({
+      text: formatText(message.message, entry),
+      time: nanos_to_string(entry.elapsedRealtimeNanos),
+      tag: viewerConfig.groups[message.group].tag,
+      level: message.level,
+      at: message.at,
+      timestamp: entry.elapsedRealtimeNanos,
+    });
+  }
+}
+
+function formatText(messageFormat, data) {
+  let out = '';
+  const strParams = data.strParams;
+  let strParamsIdx = 0;
+  const sint64Params = data.sint64Params;
+  let sint64ParamsIdx = 0;
+  const doubleParams = data.doubleParams;
+  let doubleParamsIdx = 0;
+  const booleanParams = data.booleanParams;
+  let booleanParamsIdx = 0;
+  for (let i = 0; i < messageFormat.length;) {
+    if (messageFormat[i] == '%') {
+      if (i + 1 >= messageFormat.length) {
+        // Should never happen - protologtool checks for that
+        throw new Error('Invalid format string');
+      }
+      switch (messageFormat[i + 1]) {
+        case '%':
+          out += '%';
+          break;
+        case 'd':
+          out += getParam(sint64Params, sint64ParamsIdx++).toString(10);
+          break;
+        case 'o':
+          out += getParam(sint64Params, sint64ParamsIdx++).toString(8);
+          break;
+        case 'x':
+          out += getParam(sint64Params, sint64ParamsIdx++).toString(16);
+          break;
+        case 'f':
+          out += getParam(doubleParams, doubleParamsIdx++).toFixed(6);
+          break;
+        case 'e':
+          out += getParam(doubleParams, doubleParamsIdx++).toExponential();
+          break;
+        case 'g':
+          out += getParam(doubleParams, doubleParamsIdx++).toString();
+          break;
+        case 's':
+          out += getParam(strParams, strParamsIdx++);
+          break;
+        case 'b':
+          out += getParam(booleanParams, booleanParamsIdx++).toString();
+          break;
+        default:
+          // Should never happen - protologtool checks for that
+          throw new Error('Invalid format string conversion: ' +
+            messageFormat[i + 1]);
+      }
+      i += 2;
+    } else {
+      out += messageFormat[i];
+      i += 1;
+    }
+  }
+  return out;
+}
+
+function getParam(arr, idx) {
+  if (arr.length <= idx) {
+    throw new Error('No param for format string conversion');
+  }
+  return arr[idx];
+}
\ No newline at end of file
diff --git a/tools/winscope/src/traces/ScreenRecording.ts b/tools/winscope/src/traces/ScreenRecording.ts
new file mode 100644
index 0000000..c781d7d
--- /dev/null
+++ b/tools/winscope/src/traces/ScreenRecording.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class ScreenRecording extends TraceBase {
+  screenRecordingFile: any;
+
+  constructor(files) {
+    const screenRecordingFile = files[FILE_TYPES.SCREEN_RECORDING];
+    super(screenRecordingFile.data, screenRecordingFile.timeline, files);
+
+    this.screenRecordingFile = screenRecordingFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.SCREEN_RECORDING;
+  }
+}
diff --git a/tools/winscope/src/traces/SurfaceFlinger.ts b/tools/winscope/src/traces/SurfaceFlinger.ts
new file mode 100644
index 0000000..41fae14
--- /dev/null
+++ b/tools/winscope/src/traces/SurfaceFlinger.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class SurfaceFlinger extends TraceBase {
+  sfTraceFile: any;
+
+  constructor(files) {
+    const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE];
+    super(sfTraceFile.data, sfTraceFile.timeline, files);
+
+    this.sfTraceFile = sfTraceFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.SURFACE_FLINGER;
+  }
+}
diff --git a/tools/winscope/src/traces/SystemUI.ts b/tools/winscope/src/traces/SystemUI.ts
new file mode 100644
index 0000000..4bc28d4
--- /dev/null
+++ b/tools/winscope/src/traces/SystemUI.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class SystemUI extends TraceBase {
+  systemUIFile: any;
+
+  constructor(files) {
+    const systemUIFile = files[FILE_TYPES.SYSTEM_UI];
+    super(systemUIFile.data, systemUIFile.timeline, files);
+
+    this.systemUIFile = systemUIFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.SYSTEM_UI;
+  }
+}
diff --git a/tools/winscope/src/traces/TraceBase.ts b/tools/winscope/src/traces/TraceBase.ts
new file mode 100644
index 0000000..652dc7e
--- /dev/null
+++ b/tools/winscope/src/traces/TraceBase.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+type File = {
+  blobUrl: string,
+  filename: string,
+}
+
+import JSZip from 'jszip';
+
+export default abstract class Trace implements ITrace {
+  selectedIndex: Number;
+  data: Object;
+  timeline: Array<Number>;
+  _files: File[];
+
+  constructor(data: any, timeline: Number[], files: any[]) {
+    this.selectedIndex = 0;
+    this.data = data;
+    this.timeline = timeline;
+    this._files = files;
+  }
+
+  get files(): File[] {
+    return Object.values(this._files).flat();
+  }
+
+  abstract get type(): String;
+
+  get blobUrl() {
+    if (this.files.length == 0) {
+      return null;
+    }
+
+    if (this.files.length == 1) {
+      return this.files[0].blobUrl;
+    }
+
+    const zip = new JSZip();
+
+    return (async () => {
+      for (const file of this.files) {
+        const blob = await fetch(file.blobUrl).then((r) => r.blob());
+        zip.file(file.filename, blob);
+      }
+
+      return await zip.generateAsync({ type: 'blob' });
+    })();
+
+  }
+}
+
+interface ITrace {
+  files: Array<Object>;
+  type: String,
+}
\ No newline at end of file
diff --git a/tools/winscope/src/traces/Transactions.ts b/tools/winscope/src/traces/Transactions.ts
new file mode 100644
index 0000000..c4e0085
--- /dev/null
+++ b/tools/winscope/src/traces/Transactions.ts
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+export default class Transactions extends TraceBase {
+  transactionsFile: Object;
+  transactionHistory: TransactionHistory;
+
+  constructor(files: any[]) {
+    const transactionsFile = files[FILE_TYPES.TRANSACTIONS_TRACE];
+
+    super(transactionsFile.data, transactionsFile.timeline, files);
+
+    this.transactionsFile = transactionsFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.TRANSACTION;
+  }
+}
+
+class TransactionHistory {
+  history: Object;
+  applied: Object;
+  mergeTrees: any;
+
+  constructor(transactionsEventsFiles) {
+    this.history = {};
+    this.applied = {};
+
+    if (!transactionsEventsFiles) {
+      return;
+    }
+
+    for (const eventsFile of transactionsEventsFiles) {
+      for (const event of eventsFile.data) {
+        if (event.merge) {
+          const merge = event.merge;
+          const originalId = merge.originalTransaction.id;
+          const mergedId = merge.mergedTransaction.id;
+
+          this.addMerge(originalId, mergedId);
+        } else if (event.apply) {
+          this.addApply(event.apply.tx_id);
+        }
+      }
+    }
+  }
+
+  addMerge(originalId, mergedId) {
+    const merge = new Merge(originalId, mergedId, this.history);
+    this.addToHistoryOf(originalId, merge);
+  }
+
+  addApply(transactionId) {
+    this.applied[transactionId] = true;
+    this.addToHistoryOf(transactionId, new Apply(transactionId));
+  }
+
+  addToHistoryOf(transactionId, event) {
+    if (!this.history[transactionId]) {
+      this.history[transactionId] = [];
+    }
+    this.history[transactionId].push(event);
+  }
+
+  generateHistoryTreesOf(transactionId) {
+    return this._generateHistoryTree(transactionId);
+  }
+
+  _generateHistoryTree(transactionId, upTo = null) {
+    if (!this.history[transactionId]) {
+      return [];
+    }
+
+    const children = [];
+    const events = this.history[transactionId];
+    for (let i = 0; i < (upTo ?? events.length); i++) {
+      const event = events[i];
+
+      if (event instanceof Merge) {
+        const historyTree = this.
+          _generateHistoryTree(event.mergedId, event.mergedAt);
+        const mergeTreeNode = new MergeTreeNode(event.mergedId, historyTree);
+        children.push(mergeTreeNode);
+      } else if (event instanceof Apply) {
+        children.push(new ApplyTreeNode());
+      } else {
+        throw new Error('Unhandled event type');
+      }
+    }
+
+    return children;
+  }
+
+  /**
+   * Generates the list of all the transactions that have ever been merged into
+   * the target transaction directly or indirectly through the merges of
+   * transactions that ended up being merged into the transaction.
+   * This includes both merges that occur before and after the transaction is
+   * applied.
+   * @param {Number} transactionId - The id of the transaction we want the list
+   *                                 of transactions merged in for
+   * @return {Set<Number>} a set of all the transaction ids that are in the
+   *                       history of merges of the transaction
+   */
+  allTransactionsMergedInto(transactionId) {
+    const allTransactionsMergedIn = new Set();
+
+    let event;
+    const toVisit = this.generateHistoryTreesOf(transactionId);
+    while (event = toVisit.pop()) {
+      if (event instanceof MergeTreeNode) {
+        allTransactionsMergedIn.add(event.mergedId);
+        for (const child of event.children) {
+          toVisit.push(child);
+        }
+      }
+    }
+
+    return allTransactionsMergedIn;
+  }
+
+  /**
+   * Generated the list of transactions that have been directly merged into the
+   * target transaction those are transactions that have explicitly been merged
+   * in the code with a call to merge.
+   * @param {Number} transactionId - The id of the target transaction.
+   * @return {Array<Number>} an array of the transaction ids of the transactions
+   *                        directly merged into the target transaction
+   */
+  allDirectMergesInto(transactionId) {
+    return (this.history[transactionId] ?? [])
+      .filter((event) => event instanceof Merge)
+      .map((merge) => merge.mergedId);
+  }
+}
+
+class MergeTreeNode {
+  mergedId: Number;
+  mergedTransactionHistory: TransactionHistory;
+  children: TransactionHistory[];
+
+  constructor(mergedId, mergedTransactionHistory) {
+    this.mergedId = mergedId;
+    this.mergedTransactionHistory = mergedTransactionHistory;
+    this.children = mergedTransactionHistory;
+  }
+
+  get type() {
+    return 'merge';
+  }
+}
+
+class ApplyTreeNode {
+  children: any[];
+
+  constructor() {
+    this.children = [];
+  }
+
+  get type() {
+    return 'apply';
+  }
+}
+
+class Merge {
+  originalId: Number;
+  mergedId: Number;
+  mergedAt: Number;
+
+  constructor(originalId, mergedId, history) {
+    this.originalId = originalId;
+    this.mergedId = mergedId;
+    // Specifies how long the merge chain of the merged transaction was at the
+    // time is was merged.
+    this.mergedAt = history[mergedId]?.length ?? 0;
+  }
+}
+
+class Apply {
+  transactionId: Number;
+
+  constructor(transactionId) {
+    this.transactionId = transactionId;
+  }
+}
+
+/**
+ * Converts the transactionId to the values that compose the identifier.
+ * The top 32 bits is the PID of the process that created the transaction
+ * and the bottom 32 bits is the ID of the transaction unique within that
+ * process.
+ * @param {Number} transactionId
+ * @return {Object} An object containing the id and pid of the transaction.
+ */
+export function expandTransactionId(transactionId) {
+  // Can't use bit shift operation because it isn't a 32 bit integer...
+  // Because js uses floating point numbers for everything, maths isn't 100%
+  // accurate so we need to round...
+  return Object.freeze({
+    id: Math.round(transactionId % Math.pow(2, 32)),
+    pid: Math.round(transactionId / Math.pow(2, 32)),
+  });
+}
diff --git a/tools/winscope/src/traces/Wayland.ts b/tools/winscope/src/traces/Wayland.ts
new file mode 100644
index 0000000..b5924d8
--- /dev/null
+++ b/tools/winscope/src/traces/Wayland.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from "@/decode.js";
+import TraceBase from './TraceBase';
+
+export default class WayLand extends TraceBase {
+  waylandFile: Object;
+
+  constructor(files) {
+    const waylandFile = files[FILE_TYPES.WAYLAND_TRACE];
+    super(waylandFile.data, waylandFile.timeline, files);
+
+    this.waylandFile = waylandFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.WAYLAND;
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/src/traces/WindowManager.ts b/tools/winscope/src/traces/WindowManager.ts
new file mode 100644
index 0000000..bcf5026
--- /dev/null
+++ b/tools/winscope/src/traces/WindowManager.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
+import TraceBase from './TraceBase';
+
+import { WindowManagerTrace } from '@/flickerlib';
+
+export default class WindowManager extends TraceBase {
+  wmTraceFile: Object;
+
+  constructor(files) {
+    const wmTraceFile = files[FILE_TYPES.WINDOW_MANAGER_TRACE];
+    super(wmTraceFile.data, wmTraceFile.timeline, files);
+
+    this.wmTraceFile = wmTraceFile;
+  }
+
+  get type() {
+    return TRACE_TYPES.WINDOW_MANAGER;
+  }
+
+  static fromProto(proto): WindowManagerTrace {
+    return WindowManagerTrace.fromProto(proto);
+  }
+}
diff --git a/tools/winscope/src/transform.js b/tools/winscope/src/transform.js
index 8a0af18..34556dd 100644
--- a/tools/winscope/src/transform.js
+++ b/tools/winscope/src/transform.js
@@ -14,150 +14,419 @@
  * limitations under the License.
  */
 
+import {DiffType} from './utils/diff.js';
+import intDefMapping from
+  '../../../../prebuilts/misc/common/winscope/intDefMapping.json';
+
 // kind - a type used for categorization of different levels
 // name - name of the node
-// children - list of child entries. Each child entry is pair list [raw object, nested transform function].
+// children - list of child entries. Each child entry is pair list
+//            [raw object, nested transform function].
 // bounds - used to calculate the full bounds of parents
 // stableId - unique id for an entry. Used to maintain selection across frames.
-function transform({obj, kind, name, children, timestamp, rect, bounds, highlight, rects_transform, chips, visible, flattened, stableId}) {
-	function call(fn, arg) {
-		return (typeof fn == 'function') ? fn(arg) : fn;
-	}
-	function handle_children(arg, transform) {
-		return [].concat(...arg.map((item) => {
-			var childrenFunc = item[0];
-			var transformFunc = item[1];
-			var childs = call(childrenFunc, obj);
-			if (childs) {
-				if (typeof childs.map != 'function'){
-					throw 'Childs should be an array, but is: ' + (typeof childs) + '.'
-				}
-				return transform ? childs.map(transformFunc) : childs;
-			} else {
-				return [];
-			}
-		}));
-	}
-	function concat(arg, args, argsmap) {
-		var validArg = arg !== undefined && arg !== null;
+function transform({
+  obj,
+  kind,
+  name,
+  shortName,
+  children,
+  timestamp,
+  rect,
+  bounds,
+  highlight,
+  rectsTransform,
+  chips,
+  visible,
+  flattened,
+  stableId,
+  freeze = true,
+}) {
+  function call(fn, arg) {
+    return (typeof fn == 'function') ? fn(arg) : fn;
+  }
+  function handleChildren(arg, transform) {
+    return [].concat(...arg.map((item) => {
+      const childrenFunc = item[0];
+      const transformFunc = item[1];
+      const childs = call(childrenFunc, obj);
+      if (childs) {
+        if (typeof childs.map != 'function') {
+          throw new Error(
+              'Childs should be an array, but is: ' + (typeof childs) + '.');
+        }
+        return transform ? childs.map(transformFunc) : childs;
+      } else {
+        return [];
+      }
+    }));
+  }
+  function concat(arg, args, argsmap) {
+    const validArg = arg !== undefined && arg !== null;
 
-		if (Array.isArray(args)) {
-			if (validArg) {
-				return [arg].concat(...args.map(argsmap));
-			} else {
-				return [].concat(...args.map(argsmap));
-			}
-		} else if (validArg) {
-			return [arg];
-		} else {
-			return undefined;
-		}
-	}
+    if (Array.isArray(args)) {
+      if (validArg) {
+        return [arg].concat(...args.map(argsmap));
+      } else {
+        return [].concat(...args.map(argsmap));
+      }
+    } else if (validArg) {
+      return [arg];
+    } else {
+      return undefined;
+    }
+  }
 
-	var transformed_children = handle_children(children, true /* transform */);
-	rects_transform = (rects_transform === undefined) ? (e) => e : rects_transform;
+  const transformedChildren = handleChildren(children, true /* transform */);
+  rectsTransform = (rectsTransform === undefined) ? (e) => e : rectsTransform;
 
-	var kindResolved = call(kind, obj);
-	var nameResolved = call(name, obj);
-	var rectResolved = call(rect, obj);
-	var stableIdResolved = (stableId === undefined) ?
-			kindResolved + '|-|' + nameResolved :
-			call(stableId, obj);
+  const kindResolved = call(kind, obj);
+  const nameResolved = call(name, obj);
+  const shortNameResolved = call(shortName, obj);
+  const rectResolved = call(rect, obj);
+  // eslint-disable-next-line max-len
+  const stableIdResolved = (stableId === undefined) ? kindResolved + '|-|' + nameResolved : call(stableId, obj);
 
-	var result = {
-		kind: kindResolved,
-		name: nameResolved,
-		children: transformed_children,
-		obj: obj,
-		timestamp: call(timestamp, obj),
-		skip: handle_children(children, false /* transform */),
-		bounds: call(bounds, obj) || transformed_children.map((e) => e.bounds).find((e) => true) || undefined,
-		rect: rectResolved,
-		rects: rects_transform(concat(rectResolved, transformed_children, (e) => e.rects)),
-		highlight: call(highlight, obj),
-		chips: call(chips, obj),
-		stableId: stableIdResolved,
-		visible: call(visible, obj),
-		childrenVisible: transformed_children.some((c) => {
-				return c.childrenVisible || c.visible
-			}),
-		flattened: call(flattened, obj),
-	};
+  const result = {
+    kind: kindResolved,
+    name: nameResolved,
+    shortName: shortNameResolved,
+    collapsed: false,
+    children: transformedChildren,
+    obj: obj,
+    timestamp: call(timestamp, obj),
+    skip: handleChildren(children, false /* transform */),
+    bounds: call(bounds, obj) || transformedChildren.map(
+        (e) => e.bounds).find((e) => true) || undefined,
+    rect: rectResolved,
+    rects: rectsTransform(
+        concat(rectResolved, transformedChildren, (e) => e.rects)),
+    highlight: call(highlight, obj),
+    chips: call(chips, obj),
+    stableId: stableIdResolved,
+    visible: call(visible, obj),
+    childrenVisible: transformedChildren.some((c) => {
+      return c.childrenVisible || c.visible;
+    }),
+    flattened: call(flattened, obj),
+  };
 
-	if (rectResolved) {
-		rectResolved.ref = result;
-	}
+  if (rectResolved) {
+    rectResolved.ref = result;
+  }
 
-	return Object.freeze(result);
+  return freeze ? Object.freeze(result) : result;
 }
 
-
-function transform_json(obj, name, options) {
-	let {skip, formatter} = options;
-
-	var children = [];
-	var formatted = undefined;
-
-	if (skip && skip.includes(obj)) {
-		// skip
-	} else if ((formatted = formatter(obj))) {
-		children.push(transform_json(null, formatted, options));
-	} else if (Array.isArray(obj)) {
-		obj.forEach((e, i) => {
-			children.push(transform_json(e, ""+i, options));
-		})
-	} else if (typeof obj == 'string') {
-		children.push(transform_json(null, obj, options));
-	} else if (typeof obj == 'number' || typeof obj == 'boolean') {
-		children.push(transform_json(null, ""+obj, options));
-	} else if (obj && typeof obj == 'object') {
-		Object.keys(obj).forEach((key) => {
-			children.push(transform_json(obj[key], key, options));
-		});
-	}
-
-	if (children.length == 1 && !children[0].combined) {
-		return Object.freeze({
-			kind: "",
-			name: name + ": " + children[0].name,
-			children: children[0].children,
-			combined: true
-		});
-	}
-
-	return Object.freeze({
-		kind: "",
-		name: name,
-		children: children,
-	});
+function getDiff(val, compareVal) {
+  if (val && isTerminal(compareVal)) {
+    return {type: DiffType.ADDED};
+  } else if (isTerminal(val) && compareVal) {
+    return {type: DiffType.DELETED};
+  } else if (compareVal != val) {
+    return {type: DiffType.MODIFIED};
+  } else {
+    return {type: DiffType.NONE};
+  }
 }
 
+// Represents termination of the object traversal,
+// differentiated with a null value in the object.
+class Terminal { }
+
+function isTerminal(obj) {
+  return obj instanceof Terminal;
+}
+
+class ObjectTransformer {
+  constructor(obj, rootName, stableId) {
+    this.obj = obj;
+    this.rootName = rootName;
+    this.stableId = stableId;
+    this.diff = false;
+  }
+
+  setOptions(options) {
+    this.options = options;
+    return this;
+  }
+
+  withDiff(obj, fieldOptions) {
+    this.diff = true;
+    this.compareWithObj = obj ?? new Terminal();
+    this.compareWithFieldOptions = fieldOptions;
+    return this;
+  }
+
+  /**
+   * Transform the raw JS Object into a TreeView compatible object
+   * @param {Object} transformOptions detailed below
+   * @param {bool} keepOriginal whether or not to store the original object in
+   *                            the obj property of a tree node for future
+   *                            reference
+   * @param {bool} freeze whether or not the returned objected should be frozen
+   *                      to prevent changing any of its properties
+   * @param {string} metadataKey the key that contains a node's metadata to be
+   *                             accessible after the transformation
+   * @return {Object} the transformed JS object compatible with treeviews.
+   */
+  transform(transformOptions = {
+    keepOriginal: false, freeze: true, metadataKey: null,
+  }) {
+    const {formatter} = this.options;
+    if (!formatter) {
+      throw new Error('Missing formatter, please set with setOptions()');
+    }
+
+    return this._transform(this.obj, this.rootName, null,
+        this.compareWithObj, this.rootName, null,
+        this.stableId, transformOptions);
+  }
+
+  /**
+   * @param {Object} obj the object to transform to a treeview compatible object
+   * @param {Object} fieldOptions options on how to transform fields
+   * @param {*} metadataKey if 'obj' contains this key, it is excluded from the
+   *                        transformation
+   * @return {Object} the transformed JS object compatible with treeviews.
+   */
+  _transformObject(obj, fieldOptions, metadataKey) {
+    const {skip, formatter} = this.options;
+    const transformedObj = {
+      obj: {},
+      fieldOptions: {},
+    };
+    let formatted = undefined;
+
+    if (skip && skip.includes(obj)) {
+      // skip
+    } else if ((formatted = formatter(obj))) {
+      // Obj has been formatted into a terminal node — has no children.
+      transformedObj.obj[formatted] = new Terminal();
+      transformedObj.fieldOptions[formatted] = fieldOptions;
+    } else if (Array.isArray(obj)) {
+      obj.forEach((e, i) => {
+        transformedObj.obj['' + i] = e;
+        transformedObj.fieldOptions['' + i] = fieldOptions;
+      });
+    } else if (typeof obj == 'string') {
+      // Object is a primitive type — has no children. Set to terminal
+      // to differentiate between null object and Terminal element.
+      transformedObj.obj[obj] = new Terminal();
+      transformedObj.fieldOptions[obj] = fieldOptions;
+    } else if (typeof obj == 'number' || typeof obj == 'boolean') {
+      // Similar to above — primitive type node has no children.
+      transformedObj.obj['' + obj] = new Terminal();
+      transformedObj.fieldOptions['' + obj] = fieldOptions;
+    } else if (obj && typeof obj == 'object') {
+      Object.keys(obj).forEach((key) => {
+        if (key === metadataKey) {
+          return;
+        }
+        transformedObj.obj[key] = obj[key];
+        transformedObj.fieldOptions[key] = obj.$type?.fields[key]?.options;
+      });
+    } else if (obj === null) {
+      // Null object is a has no children — set to be terminal node.
+      transformedObj.obj.null = new Terminal();
+      transformedObj.fieldOptions.null = undefined;
+    }
+
+    return transformedObj;
+  }
+
+  /**
+   * Extract the value of obj's property with key 'metadataKey'
+   * @param {Object} obj the obj we want to extract the metadata from
+   * @param {string} metadataKey the key that stores the metadata in the object
+   * @return {Object} the metadata value or null in no metadata is present
+   */
+  _getMetadata(obj, metadataKey) {
+    if (metadataKey && obj[metadataKey]) {
+      const metadata = obj[metadataKey];
+      obj[metadataKey] = undefined;
+      return metadata;
+    } else {
+      return null;
+    }
+  }
+
+  _transform(obj, name, fieldOptions,
+      compareWithObj, compareWithName, compareWithFieldOptions,
+      stableId, transformOptions) {
+    const originalObj = obj;
+    const metadata = this._getMetadata(obj, transformOptions.metadataKey);
+
+    const children = [];
+
+    if (!isTerminal(obj)) {
+      const transformedObj =
+          this._transformObject(
+              obj, fieldOptions, transformOptions.metadataKey);
+      obj = transformedObj.obj;
+      fieldOptions = transformedObj.fieldOptions;
+    }
+    if (!isTerminal(compareWithObj)) {
+      const transformedObj =
+          this._transformObject(
+              compareWithObj, compareWithFieldOptions,
+              transformOptions.metadataKey);
+      compareWithObj = transformedObj.obj;
+      compareWithFieldOptions = transformedObj.fieldOptions;
+    }
+
+    for (const key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        let compareWithChild = new Terminal();
+        let compareWithChildName = new Terminal();
+        let compareWithChildFieldOptions = undefined;
+        if (compareWithObj.hasOwnProperty(key)) {
+          compareWithChild = compareWithObj[key];
+          compareWithChildName = key;
+          compareWithChildFieldOptions = compareWithFieldOptions[key];
+        }
+        children.push(this._transform(obj[key], key, fieldOptions[key],
+            compareWithChild, compareWithChildName,
+            compareWithChildFieldOptions,
+            `${stableId}.${key}`, transformOptions));
+      }
+    }
+
+    // Takes care of adding deleted items to final tree
+    for (const key in compareWithObj) {
+      if (!obj.hasOwnProperty(key) && compareWithObj.hasOwnProperty(key)) {
+        children.push(this._transform(new Terminal(), new Terminal(), undefined,
+            compareWithObj[key], key, compareWithFieldOptions[key],
+            `${stableId}.${key}`, transformOptions));
+      }
+    }
+
+    let transformedObj;
+    if (
+      children.length == 1 &&
+      children[0].children.length == 0 &&
+      !children[0].combined
+    ) {
+      // Merge leaf key value pairs.
+      const child = children[0];
+
+      transformedObj = {
+        kind: '',
+        name: name + ': ' + child.name,
+        stableId,
+        children: child.children,
+        combined: true,
+      };
+
+      if (this.diff) {
+        transformedObj.diff = child.diff;
+      }
+    } else {
+      transformedObj = {
+        kind: '',
+        name,
+        stableId,
+        children,
+      };
+
+      let fieldOptionsToUse = fieldOptions;
+
+      if (this.diff) {
+        const diff = getDiff(name, compareWithName);
+        transformedObj.diff = diff;
+
+        if (diff.type == DiffType.DELETED) {
+          transformedObj.name = compareWithName;
+          fieldOptionsToUse = compareWithFieldOptions;
+        }
+      }
+
+      const annotationType = fieldOptionsToUse?.['(.android.typedef)'];
+      if (annotationType) {
+        if (intDefMapping[annotationType] === undefined) {
+          console.error(
+              `Missing intDef mapping for translation for ${annotationType}`);
+        } else if (intDefMapping[annotationType].flag) {
+          transformedObj.name = `${getIntFlagsAsStrings(
+              transformedObj.name, annotationType)} (${transformedObj.name})`;
+        } else {
+          transformedObj.name = `${intDefMapping[annotationType]
+              .values[transformedObj.name]} (${transformedObj.name})`;
+        }
+      }
+    }
+
+    if (transformOptions.keepOriginal) {
+      transformedObj.obj = originalObj;
+    }
+
+    if (metadata) {
+      transformedObj[transformOptions.metadataKey] = metadata;
+    }
+
+    return transformOptions.freeze ?
+      Object.freeze(transformedObj) : transformedObj;
+  }
+}
+
+function getIntFlagsAsStrings(intFlags, annotationType) {
+  const flags = [];
+
+  const mapping = intDefMapping[annotationType].values;
+
+  // Will only contain bits that have not been associated with a flag.
+  let leftOver = intFlags;
+
+  for (const intValue in mapping) {
+    if ((intFlags & parseInt(intValue)) === parseInt(intValue)) {
+      flags.push(mapping[intValue]);
+
+      leftOver = leftOver & ~intValue;
+    }
+  }
+
+  if (flags.length === 0) {
+    console.error('No valid flag mappings found for ',
+        intFlags, 'of type', annotationType);
+  }
+
+  if (leftOver) {
+    // If 0 is a valid flag value that isn't in the intDefMapping
+    // it will be ignored
+    flags.push(leftOver);
+  }
+
+  return flags.join(' | ');
+}
+
+// eslint-disable-next-line camelcase
 function nanos_to_string(elapsedRealtimeNanos) {
-	var units = [
-		[1000000, '(ns)'],
-		[1000, 'ms'],
-		[60, 's'],
-		[60, 'm'],
-		[24, 'h'],
-		[Infinity, 'd'],
-	];
+  const units = [
+    [1000000, '(ns)'],
+    [1000, 'ms'],
+    [60, 's'],
+    [60, 'm'],
+    [24, 'h'],
+    [Infinity, 'd'],
+  ];
 
-	var parts = []
-	units.some(([div, str], i) => {
-		var part = (elapsedRealtimeNanos % div).toFixed()
-		if (!str.startsWith('(')) {
-			parts.push(part + str);
-		}
-		elapsedRealtimeNanos = Math.floor(elapsedRealtimeNanos / div);
-		return elapsedRealtimeNanos == 0;
-	});
+  const parts = [];
+  units.some(([div, str], i) => {
+    const part = (elapsedRealtimeNanos % div).toFixed();
+    if (!str.startsWith('(')) {
+      parts.push(part + str);
+    }
+    elapsedRealtimeNanos = Math.floor(elapsedRealtimeNanos / div);
+    return elapsedRealtimeNanos == 0;
+  });
 
-	return parts.reverse().join('');
+  return parts.reverse().join('');
 }
 
- // Returns a UI element used highlight a visible entry.
- function get_visible_chip() {
-	return {short: 'V', long: "visible", class: 'default'};
- }
+// Returns a UI element used highlight a visible entry.
+// eslint-disable-next-line camelcase
+function get_visible_chip() {
+  return {short: 'V', long: 'visible', class: 'default'};
+}
 
-export {transform, transform_json, nanos_to_string, get_visible_chip};
+// eslint-disable-next-line camelcase
+export {transform, ObjectTransformer, nanos_to_string, get_visible_chip};
diff --git a/tools/winscope/src/transform_ime.js b/tools/winscope/src/transform_ime.js
new file mode 100644
index 0000000..cd317c8
--- /dev/null
+++ b/tools/winscope/src/transform_ime.js
@@ -0,0 +1,123 @@
+import {nanos_to_string, transform} from './transform.js'
+
+function transform_ime_trace_clients(entries) {
+  return transform({
+    obj: entries,
+    kind: 'entries',
+    name: 'entries',
+    children: [
+      [entries.entry, transform_entry_clients]
+    ]
+  });
+}
+
+function transform_entry_clients(entry) {
+  return transform({
+    obj: entry,
+    kind: 'entry',
+    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
+    children: [
+      [[entry.client], transform_client_dump]
+    ],
+    timestamp: entry.elapsedRealtimeNanos,
+    stableId: 'entry'
+  });
+}
+
+function transform_client_dump(entry) {
+  return transform({
+    obj: transform_input_connection_call(entry),
+    kind: 'Client',
+    name: '\n- methodId ' + entry?.inputMethodManager?.curId
+        + '\n- view ' + entry?.viewRootImpl?.view
+        + '\n- packageName ' + entry?.editorInfo?.packageName,
+    children: [],
+    stableId: 'client'
+  });
+}
+
+function transform_ime_trace_service(entries) {
+  return transform({
+    obj: entries,
+    kind: 'entries',
+    name: 'entries',
+    children: [
+      [entries.entry, transform_entry_service]
+    ]
+  });
+}
+
+function transform_entry_service(entry) {
+  return transform({
+    obj: entry,
+    kind: 'entry',
+    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
+    children: [
+      [[entry.inputMethodService], transform_service_dump]
+    ],
+    timestamp: entry.elapsedRealtimeNanos,
+    stableId: 'entry'
+  });
+}
+
+function transform_service_dump(entry) {
+  return transform({
+    obj: transform_input_connection_call(entry),
+    kind: 'InputMethodService',
+    name: '\n- windowVisible ' + entry?.windowVisible
+        + '\n- decorViewVisible ' + entry?.decorViewVisible
+        + '\n- packageName ' + entry?.inputEditorInfo?.packageName,
+    children: [],
+    stableId: 'service'
+  });
+}
+
+function transform_ime_trace_managerservice(entries) {
+  return transform({
+    obj: entries,
+    kind: 'entries',
+    name: 'entries',
+    children: [
+      [entries.entry, transform_entry_managerservice]
+    ]
+  });
+}
+
+function transform_entry_managerservice(entry) {
+  return transform({
+    obj: entry,
+    kind: 'entry',
+    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
+    children: [
+      [[entry.inputMethodManagerService], transform_managerservice_dump]
+    ],
+    timestamp: entry.elapsedRealtimeNanos,
+    stableId: 'entry'
+  });
+}
+
+function transform_managerservice_dump(entry) {
+  return transform({
+    obj: entry,
+    kind: 'InputMethodManagerService',
+    name: '\n- methodId ' + entry?.curMethodId
+        + '\n- curFocusedWindow ' + entry?.curFocusedWindowName
+        + '\n- lastImeTargetWindow ' + entry?.lastImeTargetWindowName
+        + '\n- inputShown ' + entry?.inputShown,
+    children: [],
+    stableId: 'managerservice'
+  });
+}
+
+function transform_input_connection_call(entry) {
+  const obj = Object.assign({}, entry)
+  if (obj.inputConnectionCall) {
+    Object.getOwnPropertyNames(obj.inputConnectionCall).forEach(name => {
+        const value = Object.getOwnPropertyDescriptor(obj.inputConnectionCall, name)
+        if (!value.value) delete obj.inputConnectionCall[name]
+    })
+  }
+  return obj
+}
+
+export {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice};
diff --git a/tools/winscope/src/transform_launcher.js b/tools/winscope/src/transform_launcher.js
index 98b49a8..24a1376 100644
--- a/tools/winscope/src/transform_launcher.js
+++ b/tools/winscope/src/transform_launcher.js
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import {transform, nanos_to_string, get_visible_chip} from './transform.js'
+import { transform, nanos_to_string, get_visible_chip } from './transform.js'
 
 function transform_launcher(launcher) {
   return transform({
@@ -49,4 +49,4 @@
   });
 }
 
-export {transform_launcher_trace};
+export { transform_launcher_trace };
diff --git a/tools/winscope/src/transform_protolog.js b/tools/winscope/src/transform_protolog.js
index 89e87d5..c3f9122 100644
--- a/tools/winscope/src/transform_protolog.js
+++ b/tools/winscope/src/transform_protolog.js
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-import viewerConfig from "../../../../frameworks/base/data/etc/services.core.protolog.json"
+import viewerConfig
+  from '../../../../frameworks/base/data/etc/services.core.protolog.json';
 
-import { nanos_to_string } from './transform.js'
+import {FormattedLogMessage, UnformattedLogMessage} from '@/traces/ProtoLog.ts';
 
-const PROTOLOG_VERSION = "1.0.0"
+const PROTOLOG_VERSION = '1.0.0';
 
 class FormatStringMismatchError extends Error {
   constructor(message) {
@@ -26,109 +27,23 @@
   }
 }
 
-function get_param(arr, idx) {
-  if (arr.length <= idx) {
-    throw new FormatStringMismatchError('No param for format string conversion');
-  }
-  return arr[idx];
-}
-
-function format_text(messageFormat, data) {
-  let out = ""
-  const strParams = data.strParams;
-  let strParamsIdx = 0;
-  const sint64Params = data.sint64Params;
-  let sint64ParamsIdx = 0;
-  const doubleParams = data.doubleParams;
-  let doubleParamsIdx = 0;
-  const booleanParams = data.booleanParams;
-  let booleanParamsIdx = 0;
-  for (let i = 0; i < messageFormat.length;) {
-    if (messageFormat[i] == '%') {
-      if (i + 1 >= messageFormat.length) {
-        // Should never happen - protologtool checks for that
-        throw new Error("Invalid format string")
-      }
-      switch (messageFormat[i + 1]) {
-        case '%':
-          out += '%';
-          break;
-        case 'd':
-          out += get_param(sint64Params, sint64ParamsIdx++).toString(10);
-          break;
-        case 'o':
-          out += get_param(sint64Params, sint64ParamsIdx++).toString(8);
-          break;
-        case 'x':
-          out += get_param(sint64Params, sint64ParamsIdx++).toString(16);
-          break;
-        case 'f':
-          out += get_param(doubleParams, doubleParamsIdx++).toFixed(6);
-          break;
-        case 'e':
-          out += get_param(doubleParams, doubleParamsIdx++).toExponential();
-          break;
-        case 'g':
-          out += get_param(doubleParams, doubleParamsIdx++).toString();
-          break;
-        case 's':
-          out += get_param(strParams, strParamsIdx++);
-          break;
-        case 'b':
-          out += get_param(booleanParams, booleanParamsIdx++).toString();
-          break;
-        default:
-          // Should never happen - protologtool checks for that
-          throw new Error("Invalid format string conversion: " + messageFormat[i + 1]);
-      }
-      i += 2;
-    } else {
-      out += messageFormat[i];
-      i += 1;
-    }
-  }
-  return out;
-}
-
-function transform_unformatted(entry) {
-  return {
-    text: (entry.messageHash.toString() + ' - [' + entry.strParams.toString() +
-      '] [' + entry.sint64Params.toString() + '] [' + entry.doubleParams.toString() +
-      '] [' + entry.booleanParams.toString() + ']'),
-    time: nanos_to_string(entry.elapsedRealtimeNanos),
-    tag: "INVALID",
-    at: "",
-    timestamp: entry.elapsedRealtimeNanos,
-  };
-}
-
-function transform_formatted(entry, message) {
-  return {
-    text: format_text(message.message, entry),
-    time: nanos_to_string(entry.elapsedRealtimeNanos),
-    tag: viewerConfig.groups[message.group].tag,
-    at: message.at,
-    timestamp: entry.elapsedRealtimeNanos,
-  };
-}
-
-function transform_message(entry) {
-  let message = viewerConfig.messages[entry.messageHash]
+function transformMessage(entry) {
+  const message = viewerConfig.messages[entry.messageHash];
   if (message === undefined) {
-    return transform_unformatted(entry);
+    return new FormattedLogMessage(entry);
   } else {
     try {
-      return transform_formatted(entry, message);
+      return new UnformattedLogMessage(entry, message);
     } catch (err) {
       if (err instanceof FormatStringMismatchError) {
-        return transform_unformatted(entry);
+        return new FormattedLogMessage(entry);
       }
       throw err;
     }
   }
 }
 
-function transform_protolog(log) {
+function transformProtolog(log) {
   if (log.version !== PROTOLOG_VERSION) {
     throw new Error('Unsupported log version');
   }
@@ -136,12 +51,14 @@
     throw new Error('Unsupported viewer config version');
   }
 
-  let data = log.log.map(entry => (transform_message(entry)))
-  data.sort(function(a, b) { return a.timestamp - b.timestamp })
-  let transformed = {
-    children: data
-  }
-  return transformed
+  const data = log.log.map((entry) => (transformMessage(entry)));
+  data.sort(function(a, b) {
+    return a.timestamp - b.timestamp;
+  });
+  const transformed = {
+    children: data,
+  };
+  return transformed;
 }
 
-export { transform_protolog };
+export {transformProtolog};
diff --git a/tools/winscope/src/transform_sf.js b/tools/winscope/src/transform_sf.js
index 7b631c6..8d6c20d 100644
--- a/tools/winscope/src/transform_sf.js
+++ b/tools/winscope/src/transform_sf.js
@@ -14,27 +14,41 @@
  * limitations under the License.
  */
 
-import {transform, nanos_to_string, get_visible_chip} from './transform.js'
-import { fill_occlusion_state, fill_inherited_state } from './sf_visibility.js';
+// eslint-disable-next-line camelcase
+import {transform, nanos_to_string, get_visible_chip} from './transform.js';
+// eslint-disable-next-line camelcase
+import {fill_occlusion_state, fill_inherited_state} from './sf_visibility.js';
+import {getSimplifiedLayerName} from './utils/names';
 
-var RELATIVE_Z_CHIP = {short: 'RelZ',
-    long: "Is relative Z-ordered to another surface",
-    class: 'warn'};
-var RELATIVE_Z_PARENT_CHIP = {short: 'RelZParent',
-    long: "Something is relative Z-ordered to this surface",
-    class: 'warn'};
-var MISSING_LAYER = {short: 'MissingLayer',
-    long: "This layer was referenced from the parent, but not present in the trace",
-    class: 'error'};
-var GPU_CHIP = {short: 'GPU',
-    long: "This layer was composed on the GPU",
-    class: 'gpu'};
-var HWC_CHIP = {short: 'HWC',
-    long: "This layer was composed by Hardware Composer",
-    class: 'hwc'};
+const RELATIVE_Z_CHIP = {
+  short: 'RelZ',
+  long: 'Is relative Z-ordered to another surface',
+  class: 'warn',
+};
+const RELATIVE_Z_PARENT_CHIP = {
+  short: 'RelZParent',
+  long: 'Something is relative Z-ordered to this surface',
+  class: 'warn',
+};
+const MISSING_LAYER = {
+  short: 'MissingLayer',
+  long:
+    'This layer was referenced from the parent, but not present in the trace',
+  class: 'error',
+};
+const GPU_CHIP = {
+  short: 'GPU',
+  long: 'This layer was composed on the GPU',
+  class: 'gpu',
+};
+const HWC_CHIP = {
+  short: 'HWC',
+  long: 'This layer was composed by Hardware Composer',
+  class: 'hwc',
+};
 
-function transform_layer(layer) {
-  function offset_to(bounds, x, y) {
+function transformLayer(layer) {
+  function offsetTo(bounds, x, y) {
     return {
       right: bounds.right - (bounds.left - x),
       bottom: bounds.bottom - (bounds.top - y),
@@ -43,11 +57,10 @@
     };
   }
 
-  function get_rect(layer) {
-    var result = layer.bounds;
-    var tx = layer.position ? layer.position.x || 0 : 0;
-    var ty = layer.position ? layer.position.y || 0 : 0;
-    result = offset_to(result, 0, 0);
+  function getRect(layer) {
+    let result = layer.bounds;
+    const tx = layer.position ? layer.position.x || 0 : 0;
+    const ty = layer.position ? layer.position.y || 0 : 0;
     result.label = layer.name;
     result.transform = layer.transform;
     result.transform.tx = tx;
@@ -55,15 +68,16 @@
     return result;
   }
 
-  function add_hwc_composition_type_chip(layer) {
-      if (layer.hwcCompositionType === "CLIENT") {
-          chips.push(GPU_CHIP);
-      } else if (layer.hwcCompositionType === "DEVICE" || layer.hwcCompositionType === "SOLID_COLOR") {
-          chips.push(HWC_CHIP);
-      }
+  function addHwcCompositionTypeChip(layer) {
+    if (layer.hwcCompositionType === 'CLIENT') {
+      chips.push(GPU_CHIP);
+    } else if (layer.hwcCompositionType === 'DEVICE' ||
+        layer.hwcCompositionType === 'SOLID_COLOR') {
+      chips.push(HWC_CHIP);
+    }
   }
 
-  var chips = [];
+  const chips = [];
   if (layer.visible) {
     chips.push(get_visible_chip());
   }
@@ -76,36 +90,52 @@
   if (layer.missing) {
     chips.push(MISSING_LAYER);
   }
-  add_hwc_composition_type_chip(layer);
-  const rect = layer.visible ? get_rect(layer) : undefined;
+  addHwcCompositionTypeChip(layer);
 
-  return transform({
+  const rect = layer.visible && layer.bounds !== null ?
+      getRect(layer) : undefined;
+
+  const simplifiedLayerName = getSimplifiedLayerName(layer.name);
+  const shortName = simplifiedLayerName ?
+      layer.id + ': ' + simplifiedLayerName : undefined;
+
+  const transformedLayer = transform({
     obj: layer,
     kind: '',
-    name: layer.id + ": " + layer.name,
-    children: [[layer.resolvedChildren, transform_layer]],
+    name: layer.id + ': ' + layer.name,
+    shortName,
+    children: [[layer.resolvedChildren, transformLayer]],
     rect,
     undefined /* bounds */,
     highlight: rect,
     chips,
     visible: layer.visible,
+    freeze: false,
   });
+
+  // NOTE: Temporary until refactored to use flickerlib
+  transformedLayer.invisibleDueTo = layer.invisibleDueTo;
+  transformedLayer.occludedBy = layer.occludedBy;
+  transformedLayer.partiallyOccludedBy = layer.partiallyOccludedBy;
+  transformedLayer.coveredBy = layer.coveredBy;
+
+  return Object.freeze(transformedLayer);
 }
- 
+
 function missingLayer(childId) {
   return {
-    name: "layer #" + childId,
+    name: 'layer #' + childId,
     missing: true,
     zOrderRelativeOf: -1,
-    transform: {dsdx:1, dtdx:0, dsdy:0, dtdy:1},
-  }
+    transform: {dsdx: 1, dtdx: 0, dsdy: 0, dtdy: 1},
+  };
 }
 
-function transform_layers(includesCompositionState, layers) {
-  var idToItem = {};
-  var isChild = {}
+function transformLayers(includesCompositionState, layers) {
+  const idToItem = {};
+  const isChild = {};
 
-  var layersList = layers.layers || [];
+  const layersList = layers.layers || [];
 
   layersList.forEach((e) => {
     idToItem[e.id] = e;
@@ -119,14 +149,21 @@
         isChild[childId] = true;
       });
     }
-    if ((e.zOrderRelativeOf || -1) !== -1) {
+    // We don't clean up relatives when the relative parent is removed, so it
+    // may be inconsistent
+    if ((e.zOrderRelativeOf || -1) !== -1 && (idToItem[e.zOrderRelativeOf])) {
       idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
     }
   });
 
-  var roots = layersList.filter((e) => !isChild[e.id]);
+  const roots = layersList.filter((e) => !isChild[e.id]);
   fill_inherited_state(idToItem, roots);
-  fill_occlusion_state(idToItem, roots, includesCompositionState);
+
+  // Backwards compatibility check
+  const occlusionDetectionCompatible = roots[0].bounds !== null;
+  if (occlusionDetectionCompatible) {
+    fill_occlusion_state(idToItem, roots, includesCompositionState);
+  }
   function foreachTree(nodes, fun) {
     nodes.forEach((n) => {
       fun(n);
@@ -134,15 +171,17 @@
     });
   }
 
-  var idToTransformed = {};
-  var transformed_roots = roots.map((r) =>
-    transform_layer(r, {parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
-      parentHidden: null}));
+  const idToTransformed = {};
+  const transformedRoots = roots.map((r) =>
+    transformLayer(r, {
+      parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
+      parentHidden: null,
+    }));
 
-  foreachTree(transformed_roots, (n) => {
+  foreachTree(transformedRoots, (n) => {
     idToTransformed[n.obj.id] = n;
   });
-  var flattened = [];
+  const flattened = [];
   layersList.forEach((e) => {
     flattened.push(idToTransformed[e.id]);
   });
@@ -152,10 +191,10 @@
     kind: 'layers',
     name: 'layers',
     children: [
-      [transformed_roots, (c) => c],
+      [transformedRoots, (c) => c],
     ],
-    rects_transform (r) {
-      var res = [];
+    rectsTransform(r) {
+      const res = [];
       flattened.forEach((l) => {
         if (l.rect) {
           res.push(l.rect);
@@ -167,31 +206,34 @@
   });
 }
 
-function transform_layers_entry(entry) {
+function transformLayersEntry(entry) {
   const includesCompositionState = !entry.excludesCompositionState;
   return transform({
     obj: entry,
     kind: 'entry',
-    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
+    name: nanos_to_string(entry.elapsedRealtimeNanos) + ' - ' + entry.where,
     children: [
-      [[entry.layers], (layer) => transform_layers(includesCompositionState, layer)],
+      [
+        [entry.layers],
+        (layer) => transformLayers(includesCompositionState, layer),
+      ],
     ],
     timestamp: entry.elapsedRealtimeNanos,
     stableId: 'entry',
   });
 }
 
-function transform_layers_trace(entries) {
-  var r = transform({
+function transformLayersTrace(entries) {
+  const r = transform({
     obj: entries,
     kind: 'layerstrace',
     name: 'layerstrace',
     children: [
-      [entries.entry, transform_layers_entry],
+      [entries.entry, transformLayersEntry],
     ],
   });
 
   return r;
 }
 
-export {transform_layers, transform_layers_trace};
+export {transformLayers, transformLayersTrace};
diff --git a/tools/winscope/src/transform_sys_ui.js b/tools/winscope/src/transform_sys_ui.js
index f636b09..3b14546 100644
--- a/tools/winscope/src/transform_sys_ui.js
+++ b/tools/winscope/src/transform_sys_ui.js
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import {transform, nanos_to_string, get_visible_chip} from './transform.js'
+import { transform, nanos_to_string, get_visible_chip } from './transform.js'
 
 function transform_edgeBack(edgeBack) {
   return transform({
@@ -31,7 +31,7 @@
     kind: 'systemUi',
     name: 'systemUi',
     children: [
-        [[sysui.edgeBackGestureHandler], transform_edgeBack]
+      [[sysui.edgeBackGestureHandler], transform_edgeBack]
     ]
   });
 }
@@ -60,4 +60,4 @@
   });
 }
 
-export {transform_sysui_trace};
+export { transform_sysui_trace };
diff --git a/tools/winscope/src/transform_transaction.js b/tools/winscope/src/transform_transaction.js
index da40bd1..ef958f8 100644
--- a/tools/winscope/src/transform_transaction.js
+++ b/tools/winscope/src/transform_transaction.js
@@ -14,53 +14,70 @@
  * limitations under the License.
  */
 
-import {transform, nanos_to_string} from './transform.js'
+import {nanos_to_string} from './transform.js';
 
-function transform_transaction(transaction) {
-  return transform({
-    obj: transaction,
-    kind: 'transaction',
-    children:[[transaction.surfaceChange, transform_entry_type('surfaceChange')],
-        [transaction.displayChange, transform_entry_type('displayChange')]],
-    rects: [],
-    visible: false,
-  })
-}
+function transform_transaction(transaction, layerIdToName) {
+  const transactions = [];
 
-function transform_entry_type(transactionType) {
-  function return_transform(item) {
-    return Object.freeze({
-      obj: item,
-      kind: transactionType,
-      rects: [],
-      visible: false,
-      name: item.name || item.id || nanos_to_string(item.when),
-    });
+  for (const surfaceChange of transaction.surfaceChange) {
+    transactions.push(Object.freeze({
+      type: 'surfaceChange',
+      obj: surfaceChange,
+      layerName: layerIdToName[surfaceChange.id],
+    }));
   }
-  return transactionType === 'transaction' ? transform_transaction : return_transform;
+
+  for (const displayChange of transaction.displayChange) {
+    transactions.push(Object.freeze({
+      type: 'displayChange',
+      obj: displayChange,
+      layerName: layerIdToName[displayChange.id],
+    }));
+  }
+
+  return transactions;
 }
 
-function transform_entry(entry) {
-  var transactionType = entry.increment;
-  return transform({
-    obj: entry,
-    kind: 'entry',
-    name: nanos_to_string(entry.timeStamp),
-    children: [[[entry[transactionType]], transform_entry_type(transactionType)]],
-    timestamp: entry.timeStamp,
-  });
+function transform_entry(entry, layerIdToName) {
+  const type = entry.increment;
+  const timestamp = entry.timeStamp;
+  const time = nanos_to_string(timestamp);
+
+  switch (type) {
+    case 'transaction':
+
+      return Object.freeze({
+        type,
+        // TODO: Rename to changes
+        transactions: transform_transaction(entry.transaction, layerIdToName),
+        synchronous: entry.transaction.synchronous,
+        animation: entry.transaction.animation,
+        identifier: entry.transaction.id,
+        time,
+        origin: entry.transaction.origin,
+        timestamp,
+      });
+
+    case 'surfaceCreation':
+      // NOTE: There is no break on purpose — we want to fall through to default
+      layerIdToName[entry[type].id] = entry[type].name;
+
+    default:
+      return Object.freeze({
+        type,
+        obj: entry[type],
+        layerName: entry[type].name ?? layerIdToName[entry[type].id],
+        time,
+        timestamp,
+      });
+  }
 }
 
 function transform_transaction_trace(entries) {
-  var r = transform({
-    obj: entries,
-    kind: 'entries',
-    name: 'transactionstrace',
-    children: [
-      [entries.increment, transform_entry],
-    ],
-  })
-  return r;
+  const layerIdToName = {};
+  const data = entries.increment.map((entry) => transform_entry(entry, layerIdToName));
+
+  return {children: data};
 }
 
 export {transform_transaction_trace};
diff --git a/tools/winscope/src/transform_wm.js b/tools/winscope/src/transform_wm.js
deleted file mode 100644
index 8a947b6..0000000
--- a/tools/winscope/src/transform_wm.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {transform, nanos_to_string, get_visible_chip} from './transform.js'
-
-function transform_window(entry) {
-  var chips = [];
-  var renderIdentifier = (id) => shortenComponentName(id.title) + "@" + id.hashCode;
-  var visible = entry.windowContainer.visible;
-  function transform_rect(rect, label) {
-    var r = rect || {};
-    return {
-        left: r.left || 0,
-        right: r.right || 0,
-        top: r.top || 0,
-        bottom: r.bottom || 0,
-        label,
-    }
-  }
-  var name = renderIdentifier(entry.identifier)
-  var rect = transform_rect((entry.windowFrames || entry).frame, name);
-
-  if (visible) {
-    chips.push(get_visible_chip());
-  } else {
-    rect = undefined;
-  }
-
-  return transform({
-    obj: entry,
-    kind: 'window',
-    name,
-    children: [
-      [entry.childWindows, transform_window],
-      [entry.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-    rect,
-    highlight: rect,
-    chips: chips,
-    visible: visible,
-  });
-}
-
-function transform_activity_record(entry) {
-  return transform({
-    obj: entry,
-    kind: 'activityRecord',
-    name: entry.name,
-    children: [
-      [entry.windowToken.windows, transform_window],
-      [entry.windowToken.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-  });
-}
-
-function transform_task(entry) {
-  return transform({
-    obj: entry,
-    kind: 'task',
-    name: entry.id || 0,
-    children: [
-      [entry.tasks, transform_task],
-      [entry.activities, transform_activity_record],
-      [entry.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-  });
-}
-
-function transform_stack(entry) {
-  return transform({
-    obj: entry,
-    kind: 'stack',
-    name: entry.id || 0,
-    children: [
-      [entry.tasks, transform_task],
-    ],
-  });
-}
-
-function transform_window_token(entry) {
-  return transform({
-    obj: entry,
-    kind: 'winToken',
-    name: '',
-    children: [
-      [entry.windows, transform_window],
-      [entry.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-  });
-}
-
-function transform_below(entry) {
-  return transform({
-    obj: entry,
-    kind: 'belowAppWindow',
-    name: '',
-    children: [
-      [entry.windows, transform_window],
-    ],
-  });
-}
-
-function transform_above(entry) {
-  return transform({
-    obj: entry,
-    kind: 'aboveAppWindow',
-    name: '',
-    children: [
-      [entry.windows, transform_window],
-    ],
-  });
-}
-
-function transform_ime(entry) {
-  return transform({
-    obj: entry,
-    kind: 'imeWindow',
-    name: '',
-    children: [
-      [entry.windows, transform_window],
-    ],
-  });
-}
-
-function transform_window_container_child(entry) {
-  if (entry.displayArea != null) {return transform_display_area(entry.displayArea)}
-  if (entry.displayContent != null) {return transform_display_content(entry.displayContent)}
-  if (entry.task != null) {return transform_task(entry.task)}
-  if (entry.activity != null) {return transform_activity_record(entry.activity)}
-  if (entry.windowToken != null) {return transform_window_token(entry.windowToken)}
-  if (entry.window != null) {return transform_window(entry.window)}
-
-  // The WindowContainerChild may be unknown
-  return transform({
-      obj: entry,
-      kind: 'WindowContainerChild',
-      name: '',
-      children: [[entry.windowContainer.children.reverse(), transform_window_container_child],]
-  });
-}
-
-
-function transform_display_area(entry) {
-  return transform({
-    obj: entry,
-    kind: 'DisplayArea',
-    name: entry.name,
-    children: [
-      [entry.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-  });
-}
-
-function transform_display_content(entry) {
-  var bounds = {
-    width: entry.displayInfo.logicalWidth || 0,
-    height: entry.displayInfo.logicalHeight || 0,
-  };
-
-  return transform({
-    obj: entry,
-    kind: 'display',
-    name: entry.id || 0,
-    children: [
-      [entry.aboveAppWindows, transform_above],
-      [entry.imeWindows, transform_ime],
-      [entry.stacks, transform_stack],
-      [entry.tasks, transform_task],
-      [entry.belowAppWindows, transform_below],
-      [entry.windowContainer.children.reverse(), transform_window_container_child],
-    ],
-    bounds,
-  });
-}
-
-function transform_policy(entry) {
-  return transform({
-    obj: entry,
-    kind: 'policy',
-    name: 'policy',
-    children: [],
-  });
-}
-
-function transform_window_service(entry) {
-  return transform({
-    obj: entry,
-    kind: 'service',
-    name: '',
-    children: [
-      [entry.rootWindowContainer.displays, transform_display_content],
-      [entry.rootWindowContainer.windowContainer.children.reverse(),
-        transform_window_container_child],
-    [[entry.policy], transform_policy],
-    ],
-    timestamp: entry.elapsedRealtimeNanos,
-  });
-}
-
-function transform_entry(entry) {
-  return transform({
-    obj: entry,
-    kind: 'entry',
-    name: nanos_to_string(entry.elapsedRealtimeNanos),
-    children: [
-      [entry.windowManagerService.rootWindowContainer.displays, transform_display_content],
-      [entry.windowManagerService.rootWindowContainer.windowContainer.children.reverse(),
-          transform_window_container_child],
-      [[entry.windowManagerService.policy], transform_policy],
-    ],
-    timestamp: entry.elapsedRealtimeNanos,
-    stableId: 'entry',
-  });
-}
-
-function transform_window_trace(entries) {
-  return transform({
-    obj: entries,
-    kind: 'entries',
-    name: 'entries',
-    children: [
-      [entries.entry, transform_entry],
-    ],
-  });
-}
-
-function shortenComponentName(name) {
-  if (!name.includes('/')) {
-    return name
-  }
-  var split = name.split('/');
-  var pkg = split[0];
-  var clazz = split.slice(1).join('/');
-  if (clazz.startsWith(pkg + '.')) {
-    clazz = clazz.slice(pkg.length + 1);
-    return [pkg, clazz].join('/');
-  }
-  return name;
-}
-
-export {transform_window_service, transform_window_trace};
diff --git a/tools/winscope/src/utils/compatibility.js b/tools/winscope/src/utils/compatibility.js
new file mode 100644
index 0000000..5685f37
--- /dev/null
+++ b/tools/winscope/src/utils/compatibility.js
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const CompatibleFeatures = {
+    DiffVisualization: true,
+}
+
+export { CompatibleFeatures }
\ No newline at end of file
diff --git a/tools/winscope/src/utils/consts.js b/tools/winscope/src/utils/consts.js
new file mode 100644
index 0000000..86799d9
--- /dev/null
+++ b/tools/winscope/src/utils/consts.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Should be kept in sync with ENUM is in Google3 under:
+ * google3/wireless/android/tools/android_bug_tool/extension/common/actions
+ */
+const WebContentScriptMessageType = {
+  UNKNOWN: 0,
+  CONVERT_OBJECT_URL: 1,
+  CONVERT_OBJECT_URL_RESPONSE: 2,
+};
+
+const NAVIGATION_STYLE = {
+  GLOBAL: 'Global',
+  FOCUSED: 'Focused',
+  CUSTOM: 'Custom',
+  TARGETED: 'Targeted',
+};
+
+const logLevel = {
+  INFO: 'info',
+  DEBUG: 'debug',
+  VERBOSE: 'verbose',
+  WARN: 'warn',
+  ERROR: 'error',
+  WTF: 'wtf',
+}
+
+export { WebContentScriptMessageType, NAVIGATION_STYLE, logLevel };
diff --git a/tools/winscope/src/utils/diff.js b/tools/winscope/src/utils/diff.js
new file mode 100644
index 0000000..7b07606
--- /dev/null
+++ b/tools/winscope/src/utils/diff.js
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO (b/162300507): Get rid of cloning
+import cloneDeep from 'lodash.clonedeep';
+
+export const DiffType = Object.freeze({
+  NONE: 'none',
+  ADDED: 'added',
+  DELETED: 'deleted',
+  ADDED_MOVE: 'addedMove',
+  DELETED_MOVE: 'deletedMove',
+  MODIFIED: 'modified',
+});
+
+export function defaultModifiedCheck(newNode, oldNode) {
+  if (!newNode && !oldNode) {
+    return false;
+  }
+
+  if ((newNode && !oldNode) || (!newNode && oldNode)) {
+    return true;
+  }
+
+  return JSON.stringify(newNode.obj) !== JSON.stringify(oldNode.obj);
+}
+
+function isPrimitive(test) {
+  return test !== Object(test);
+}
+
+export class DiffGenerator {
+  constructor(tree) {
+    if (tree.asRawTreeViewObject) {
+      this.tree = tree.asRawTreeViewObject();
+    } else {
+      this.tree = tree;
+    }
+  }
+
+  compareWith(tree) {
+    if (tree?.asRawTreeViewObject) {
+      this.diffWithTree = tree.asRawTreeViewObject();
+    } else {
+      this.diffWithTree = tree;
+    }
+
+    return this;
+  }
+
+  withUniqueNodeId(getNodeId) {
+    this.getNodeId = (node) => {
+      const id = getNodeId(node);
+      if (id === null || id === undefined) {
+        console.error('Null node ID for node', node);
+        throw new Error('Node ID can\'t be null or undefined');
+      }
+      return id;
+    };
+    return this;
+  }
+
+  withModifiedCheck(isModified) {
+    this.isModified = isModified;
+    return this;
+  }
+
+  generateDiffTree() {
+    this.newMapping = this._generateIdToNodeMapping(this.tree);
+    this.oldMapping = this.diffWithTree ?
+      this._generateIdToNodeMapping(this.diffWithTree) : {};
+
+    const diffTrees =
+      this._generateDiffTree(this.tree, this.diffWithTree, [], []);
+
+    let diffTree;
+    if (diffTrees.length > 1) {
+      diffTree = {
+        kind: '',
+        name: 'DiffTree',
+        children: diffTrees,
+        stableId: 'DiffTree',
+      };
+    } else {
+      diffTree = diffTrees[0];
+    }
+
+    return Object.freeze(diffTree);
+  }
+
+  _generateIdToNodeMapping(node, acc) {
+    acc = acc || {};
+
+    const nodeId = this.getNodeId(node);
+
+    if (acc[nodeId]) {
+      throw new Error(`Duplicate node id '${nodeId}' detected...`);
+    }
+
+    acc[this.getNodeId(node)] = node;
+
+    if (node.children) {
+      for (const child of node.children) {
+        this._generateIdToNodeMapping(child, acc);
+      }
+    }
+
+    return acc;
+  }
+
+  _objIsEmpty(obj) {
+    return Object.keys(obj).length === 0 && obj.constructor === Object;
+  }
+
+  _cloneNodeWithoutChildren(node) {
+    const clone = {};
+
+    for (const key in node) {
+      if (key !== 'children') {
+        const val = node[key];
+        clone[key] = isPrimitive(val) ? val : cloneDeep(val);
+      }
+    }
+
+    return clone;
+  }
+
+  _generateDiffTree(newTree, oldTree, newTreeSiblings, oldTreeSiblings) {
+    const diffTrees = [];
+
+    // NOTE: A null ID represents a non existent node.
+    const newId = newTree ? this.getNodeId(newTree) : null;
+    const oldId = oldTree ? this.getNodeId(oldTree) : null;
+
+    const newTreeSiblingIds = newTreeSiblings.map(this.getNodeId);
+    const oldTreeSiblingIds = oldTreeSiblings.map(this.getNodeId);
+
+    if (newTree) {
+      // Deep clone newTree omitting children field
+      // Clone is required because trees are frozen objects — we can't
+      // modify the original tree object. Also means there is no side effect.
+      const diffTree = this._cloneNodeWithoutChildren(newTree);
+
+      // Default to no changes
+      diffTree.diff = {type: DiffType.NONE};
+
+      if (newId !== oldId) {
+        // A move, addition, or deletion has occurred
+        let nextOldTree;
+
+        // Check if newTree has been added or moved
+        if (!oldTreeSiblingIds.includes(newId)) {
+          if (this.oldMapping[newId]) {
+            // Objected existed in old tree, the counter party
+            // DELETED_MOVE will be/has been flagged and added to the
+            // diffTree when visiting it in the oldTree.
+            diffTree.diff = {type: DiffType.ADDED_MOVE};
+
+            // TODO: Figure out if oldTree is ever visited now...
+
+            // Switch out oldTree for new one to compare against
+            nextOldTree = this.oldMapping[newId];
+          } else {
+            diffTree.diff = {type: DiffType.ADDED};
+
+            // TODO: Figure out if oldTree is ever visited now...
+
+            // Stop comparing against oldTree
+            nextOldTree = null;
+          }
+        }
+
+        // Check if oldTree has been deleted of moved
+        if (oldTree && !newTreeSiblingIds.includes(oldId)) {
+          const deletedTreeDiff = this._cloneNodeWithoutChildren(oldTree);
+
+          if (this.newMapping[oldId]) {
+            deletedTreeDiff.diff = {type: DiffType.DELETED_MOVE};
+
+            // Stop comparing against oldTree, will be/has been
+            // visited when object is seen in newTree
+            nextOldTree = null;
+          } else {
+            deletedTreeDiff.diff = {type: DiffType.DELETED};
+
+            // Visit all children to check if they have been deleted or moved
+            deletedTreeDiff.children = this._visitChildren(null, oldTree);
+          }
+
+          diffTrees.push(deletedTreeDiff);
+        }
+
+        oldTree = nextOldTree;
+      } else {
+        // TODO: Always have modified check and add modified tags on top of
+        // others
+        // NOTE: Doesn't check for moved and modified at the same time
+        if (this.isModified && this.isModified(newTree, oldTree)) {
+          diffTree.diff = {type: DiffType.MODIFIED};
+        }
+      }
+
+      diffTree.children = this._visitChildren(newTree, oldTree);
+      diffTrees.push(diffTree);
+    } else if (oldTree) {
+      if (!newTreeSiblingIds.includes(oldId)) {
+        // Deep clone oldTree omitting children field
+        const diffTree = this._cloneNodeWithoutChildren(oldTree);
+
+        // newTree doesn't exists, oldTree has either been moved or deleted.
+        if (this.newMapping[oldId]) {
+          diffTree.diff = {type: DiffType.DELETED_MOVE};
+        } else {
+          diffTree.diff = {type: DiffType.DELETED};
+        }
+
+        diffTree.children = this._visitChildren(null, oldTree);
+        diffTrees.push(diffTree);
+      }
+    } else {
+      throw new Error('Both newTree and oldTree are undefined...');
+    }
+
+    return diffTrees;
+  }
+
+  _visitChildren(newTree, oldTree) {
+    // Recursively traverse all children of new and old tree.
+    const diffChildren = [];
+
+    // TODO: Try replacing this with some sort of zipWith.
+    const numOfChildren = Math.max(
+        newTree?.children.length ?? 0, oldTree?.children.length ?? 0);
+    for (let i = 0; i < numOfChildren; i++) {
+      const newChild = i < newTree?.children.length ?
+        newTree.children[i] : null;
+
+      const oldChild = i < oldTree?.children.length ?
+        oldTree.children[i] : null;
+
+      const childDiffTrees = this._generateDiffTree(
+          newChild, oldChild,
+          newTree?.children ?? [], oldTree?.children ?? [],
+      );
+      diffChildren.push(...childDiffTrees);
+    }
+
+    return diffChildren;
+  }
+}
diff --git a/tools/winscope/src/utils/names.js b/tools/winscope/src/utils/names.js
new file mode 100644
index 0000000..a5ae037
--- /dev/null
+++ b/tools/winscope/src/utils/names.js
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Returns just the class name and root package information
+function getComponentClassName(componentFullName) {
+  const classParts = componentFullName.split('.');
+
+  if (classParts.length <= 3) {
+    return componentFullName;
+  }
+
+  const className = classParts.slice(-1).pop();
+
+  return `${classParts[0]}.${classParts[1]}.(...).${className}`
+}
+
+const hashCode = /([A-Fa-f0-9]{7}|[A-Fa-f0-9]{6})/;
+const packageRegex = /(([a-z][a-z_0-9]*\.)*([a-z][a-z_0-9]*))/;
+const qualifiedClassNameRegex = /(([a-z][a-z_0-9]*\.)*[A-Z_]($[A-Z_]|[\w_])*)/;
+
+const surfaceRegex =
+  new RegExp(/^Surface\(.*\)\/@0x/.source + hashCode.source +
+    / - .*/.source + "$");
+
+const moduleName =
+  new RegExp("^" +
+    "(" + packageRegex.source + /\//.source + ")?" +
+    qualifiedClassNameRegex.source +
+    /(\$.*)?/.source +
+    /(\#.*)?/.source +
+    "$");
+
+function getSimplifiedLayerName(layerName) {
+  // Get rid of prepended hash code
+  let removedHashCodePrefix = false;
+  if (new RegExp("^" + hashCode.source + " ").test(layerName)) {
+    layerName = layerName.split(" ").slice(1).join(" ");
+    removedHashCodePrefix = true;
+  }
+
+  if (/^ActivityRecord\{.*\}?(\#[0-9]+)?$/.test(layerName)) {
+    return "ActivityRecord";
+  }
+
+  if (/^WindowToken\{.*\}(\#[0-9]*)?$/.test(layerName)) {
+    return "WindowToken";
+  }
+
+  if (/^WallpaperWindowToken\{.*\}(\#[0-9]*)?$/.test(layerName)) {
+    return "WallpaperWindowToken";
+  }
+
+  if (surfaceRegex.test(layerName)) {
+    return "Surface - " + layerName.split("- ").slice(-1).pop();
+  }
+
+  if (moduleName.test(layerName)) {
+    return layerName.split(".").slice(-1).pop();
+  }
+
+  if (removedHashCodePrefix) {
+    return layerName;
+  }
+
+  return null;
+}
+
+export { getComponentClassName, getSimplifiedLayerName };
\ No newline at end of file
diff --git a/tools/winscope/src/utils/utils.js b/tools/winscope/src/utils/utils.js
new file mode 100644
index 0000000..f8ec198
--- /dev/null
+++ b/tools/winscope/src/utils/utils.js
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Find the index of the last element matching the predicate in a sorted array
+function findLastMatchingSorted(array, predicate) {
+  let a = 0;
+  let b = array.length - 1;
+  while (b - a > 1) {
+    const m = Math.floor((a + b) / 2);
+    if (predicate(array, m)) {
+      a = m;
+    } else {
+      b = m - 1;
+    }
+  }
+
+  return predicate(array, b) ? b : a;
+}
+
+// Make sure stableId is unique (makes old versions of proto compatible)
+function stableIdCompatibilityFixup(item) {
+  // For backwards compatibility
+  // (the only item that doesn't have a unique stable ID in the tree)
+  if (item.stableId === 'winToken|-|') {
+    return item.stableId + item.children[0].stableId;
+  }
+
+  return item.stableId;
+}
+
+const DIRECTION = Object.freeze({
+  BACKWARD: -1,
+  FORWARD: 1,
+});
+
+const TimeUnits = Object.freeze({
+  NANO_SECONDS: 'ns',
+  MILLI_SECONDS: 'ms',
+  SECONDS: 's',
+  MINUTES: 'm',
+  HOURS: 'h',
+  DAYS: 'd',
+});
+
+function nanosToString(elapsedRealtimeNanos, precision) {
+  const units = [
+    [1000000, TimeUnits.NANO_SECONDS],
+    [1000, TimeUnits.MILLI_SECONDS],
+    [60, TimeUnits.SECONDS],
+    [60, TimeUnits.MINUTES],
+    [24, TimeUnits.HOURS],
+    [Infinity, TimeUnits.DAYS],
+  ];
+
+  const parts = [];
+
+  let precisionThresholdReached = false;
+  units.some(([div, timeUnit], i) => {
+    const part = (elapsedRealtimeNanos % div).toFixed()
+    if (timeUnit === precision) {
+      precisionThresholdReached = true;
+    }
+    if (precisionThresholdReached) {
+      parts.push(part + timeUnit);
+    }
+    elapsedRealtimeNanos = Math.floor(elapsedRealtimeNanos / div);
+
+    return elapsedRealtimeNanos == 0;
+  });
+
+  return parts.reverse().join('');
+}
+
+export { DIRECTION, findLastMatchingSorted, stableIdCompatibilityFixup, nanosToString, TimeUnits }
\ No newline at end of file
diff --git a/tools/winscope/static/favicon.svg b/tools/winscope/static/favicon.svg
new file mode 100644
index 0000000..8a75c7f
--- /dev/null
+++ b/tools/winscope/static/favicon.svg
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   viewBox="0 0 135.46666 135.46667"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="winscope.svg">
+  <defs
+     id="defs2">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient144">
+      <stop
+         style="stop-color:#3ddb85;stop-opacity:1;"
+         offset="0"
+         id="stop140" />
+      <stop
+         style="stop-color:#3ddb85;stop-opacity:0;"
+         offset="1"
+         id="stop142" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient144"
+       id="linearGradient146"
+       x1="334.86557"
+       y1="288.25122"
+       x2="362.64478"
+       y2="405.42892"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient144"
+       id="linearGradient152"
+       gradientUnits="userSpaceOnUse"
+       x1="334.86557"
+       y1="288.25122"
+       x2="362.64478"
+       y2="405.42892" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="369.78115"
+     inkscape:cy="43.694507"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     units="px"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="2560"
+     inkscape:window-height="1399"
+     inkscape:window-x="1440"
+     inkscape:window-y="20"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     style="display:inline"
+     transform="translate(0,-161.53331)">
+    <g
+       id="g60"
+       transform="matrix(1.4161793,0,0,1.4161793,-266.89731,76.345061)">
+      <path
+         id="path46"
+         d="m 254.98635,139.13116 c -4.3276,0 -7.84784,-3.71395 -7.84784,-8.27594 0,-4.56195 4.14824,-8.27278 7.84784,-8.27285 3.6996,-9e-5 7.84855,3.71014 7.84855,8.27285 0,4.56276 -3.52025,8.27594 -7.84855,8.27594 m -36.93749,-2.38905 c -3.0836,0 -5.59192,-2.64634 -5.59192,-5.89694 0,-3.25059 2.50784,-5.89476 5.59192,-5.89476 3.08358,0 5.5919,2.64364 5.5919,5.89476 0,3.25114 -2.50781,5.89694 -5.5919,5.89694 m 52.09761,-36.05821 11.17666,-20.405632 c 0.64058,-1.172551 0.25981,-2.670449 -0.85112,-3.348238 -1.11236,-0.674958 -2.53307,-0.273633 -3.17621,0.897219 l -11.31638,20.663525 c -8.6545,-4.162927 -18.37356,-6.482308 -28.74463,-6.482308 -10.37108,0 -20.09013,2.319381 -28.74411,6.482308 L 197.17379,77.827249 c -0.64314,-1.170852 -2.06385,-1.572177 -3.17621,-0.897219 -1.11167,0.677199 -1.49261,2.175808 -0.85113,3.348238 L 204.3231,100.6839 c -19.19192,11.00346 -32.318,31.48516 -34.23828,55.68305 h 134.29993 c -1.9218,-24.19736 -15.04789,-44.67959 -34.23828,-55.68357"
+         inkscape:connector-curvature="0"
+         style="fill:#3ddb85;stroke-width:0.52547503"
+         sodipodi:nodetypes="csssccsssccccccscccccccc" />
+      <path
+         id="path48"
+         d="M -0.75490308,-4.3032616 H 466.2911 V 245.86074 H -0.75490308 Z"
+         inkscape:connector-curvature="0"
+         style="fill:none" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Layer 2"
+     transform="translate(0,-161.53331)"
+     style="display:inline">
+    <g
+       id="g150"
+       transform="matrix(1.2390981,0,0,1.2745122,-22.278191,-60.145091)">
+      <path
+         inkscape:connector-curvature="0"
+         style="font-weight:400;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;display:inline;overflow:visible;opacity:0.92156863;isolation:auto;mix-blend-mode:normal;fill:#000000;stroke-width:3.05656147"
+         d="m 93.327473,227.20793 c -11.817096,0 -21.462353,9.58571 -21.462353,21.32967 0,11.74397 9.645257,21.32967 21.462353,21.32967 10.773667,0 19.717027,-7.97589 21.216847,-18.28256 h 2.56476 c 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 h -2.56476 c -1.49999,-10.3067 -10.44318,-18.28259 -21.216847,-18.28259 z m 0,6.09419 c 8.503037,0 15.330367,6.78508 15.330367,15.23548 0,8.45041 -6.82733,15.2355 -15.330367,15.2355 -8.503052,0 -15.330212,-6.78509 -15.330212,-15.2355 0,-8.45041 6.82716,-15.23548 15.330212,-15.23548 z m 23.781607,21.32968 c -1.69335,-2e-5 -3.06609,1.36422 -3.06609,3.0471 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 0,-1.68287 -1.37273,-3.04711 -3.06607,-3.0471 z m 0,9.1413 c -1.69334,-2e-5 -3.06607,1.3642 -3.06609,3.04706 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -2e-5,-1.68286 -1.37274,-3.04707 -3.06607,-3.04706 z m 0,9.14127 c -1.69335,-2e-5 -3.06609,1.36421 -3.06609,3.04709 0,1.68288 1.37274,3.04712 3.06609,3.0471 1.69334,1e-5 3.06607,-1.36423 3.06607,-3.0471 0,-1.68287 -1.37273,-3.0471 -3.06607,-3.04709 z m 0,9.14129 c -1.69334,-2e-5 -3.06608,1.36421 -3.06609,3.04708 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 z"
+         font-weight="400"
+         overflow="visible"
+         id="path62"
+         sodipodi:nodetypes="sssccsccsssssscccccccccccscsccscsc" />
+      <path
+         transform="matrix(0.26458333,0,0,0.26458333,0,161.53331)"
+         sodipodi:nodetypes="sssssscssscs"
+         inkscape:connector-curvature="0"
+         id="path130"
+         d="m 346.52914,385.88304 c -9.74153,-1.15668 -19.18712,-4.7389 -27.36682,-10.3788 -4.29704,-2.96281 -11.19439,-9.96762 -14.26053,-14.48272 -3.09123,-4.55204 -6.34453,-11.66167 -7.89403,-17.2513 -1.63959,-5.91462 -2.25943,-17.8322 -1.2539,-24.10849 3.61484,-22.56303 20.01002,-40.31938 42.90934,-46.47177 4.28814,-1.1521 5.83599,-1.30305 13.67432,-1.33348 9.26661,-0.036 11.69398,0.2988 18.66447,2.5742 8.55206,2.79168 15.83504,7.31293 22.56577,14.00876 20.25076,20.14579 22.48959,52.06917 5.25566,74.94032 -8.89686,11.80702 -22.47121,19.85596 -37.24125,22.08226 -4.528,0.6825 -11.2641,0.87091 -15.05303,0.42102 z"
+         style="opacity:0.49673205;fill:url(#linearGradient152);fill-opacity:1;stroke:none;stroke-width:0.50507629" />
+    </g>
+  </g>
+</svg>
diff --git a/tools/winscope/tsconfig.json b/tools/winscope/tsconfig.json
new file mode 100644
index 0000000..486e68b
--- /dev/null
+++ b/tools/winscope/tsconfig.json
@@ -0,0 +1,22 @@
+{
+  "compilerOptions": {
+    "baseUrl": "./src",
+    "outDir": "./dist/",
+    "noImplicitAny": false,
+    "module": "es6",
+    "target": "es5",
+    "allowJs": false,
+    "resolveJsonModule": true,
+    "moduleResolution": "node",
+    "allowSyntheticDefaultImports": true,
+    "lib": [
+      "es2019",
+      "dom",
+    ],
+    "paths": {
+      "@/*": [
+        "*"
+      ],
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/winscope/vue.config.js b/tools/winscope/vue.config.js
new file mode 100644
index 0000000..ab7013e
--- /dev/null
+++ b/tools/winscope/vue.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+    configureWebpack: {
+      devtool: 'source-map'
+    }
+  }
\ No newline at end of file
diff --git a/tools/winscope/webpack.config.common.js b/tools/winscope/webpack.config.common.js
new file mode 100644
index 0000000..f2faddb
--- /dev/null
+++ b/tools/winscope/webpack.config.common.js
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const path = require('path');
+const fs = require('fs');
+
+const VueLoaderPlugin = require('vue-loader/lib/plugin');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const KotlinWebpackPlugin = require('@jetbrains/kotlin-webpack-plugin');
+const HtmlWebpackInlineSourcePlugin =
+  require('html-webpack-inline-source-plugin');
+
+const isDev = process.env.NODE_ENV === 'development';
+
+
+function getWaylandSafePath() {
+  const waylandPath =
+    path.resolve(__dirname, '../../../vendor/google_arc/libs/wayland_service');
+
+  if (fs.existsSync(waylandPath)) {
+    return waylandPath;
+  }
+
+  return path.resolve(__dirname, 'src/stubs');
+}
+
+const webpackConfig = {
+  entry: {
+    polyfill: '@babel/polyfill',
+    main: './src/main.js',
+  },
+  externals: {
+    _: 'lodash',
+  },
+  resolve: {
+    extensions: ['.tsx', '.ts', '.js', '.vue'],
+    alias: {
+      'vue$': isDev ? 'vue/dist/vue.runtime.js' : 'vue/dist/vue.runtime.min.js',
+      '@': path.resolve(__dirname, 'src'),
+      'WaylandSafePath': getWaylandSafePath(),
+    },
+    modules: [
+      'node_modules',
+      'kotlin_build',
+      path.resolve(__dirname, '../../..'),
+    ],
+  },
+  resolveLoader: {
+    modules: [
+      'node_modules',
+      path.resolve(__dirname, 'loaders'),
+    ],
+  },
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        include: path.resolve(__dirname, './src'),
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.tsx?$/,
+        use: 'ts-loader',
+        include: path.resolve(__dirname, './src'),
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: path.resolve(__dirname, './src'),
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.css$/,
+        use: [
+          'vue-style-loader',
+          {loader: 'css-loader', options: {sourceMap: isDev}},
+        ],
+        include: path.resolve(__dirname, './src'),
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.proto$/,
+        loader: 'proto-loader',
+        options: {
+          paths: [
+            path.resolve(__dirname, '../../..'),
+            path.resolve(__dirname, '../../../external/protobuf/src'),
+          ],
+        },
+      },
+      {
+        test: /\.(png|jpg|gif|svg)$/,
+        loader: 'file-loader',
+        options: {
+          name: '[name].[ext]?[hash]',
+        },
+      },
+      {
+        test: /\.(ttf|otf|eot|woff|woff2)$/,
+        use: {
+          loader: 'file-loader',
+          options: {
+            name: 'fonts/[name].[ext]',
+          },
+        },
+      },
+    ],
+  },
+  plugins: [
+    new VueLoaderPlugin(),
+    new HtmlWebpackPlugin({
+      inlineSource: isDev ? false : '.(js|css)',
+      template: 'src/index_template.html',
+    }),
+    new HtmlWebpackInlineSourcePlugin(),
+    new KotlinWebpackPlugin({
+      src: [
+        path.join(__dirname, '../../../platform_testing/libraries/flicker/' +
+          'src/com/android/server/wm/flicker/common/'),
+      ],
+      output: 'kotlin_build',
+      moduleName: 'flicker',
+      librariesAutoLookup: true,
+      sourceMaps: true,
+      sourceMapEmbedSources: 'always',
+      verbose: true,
+      optimize: true,
+    }),
+  ],
+};
+
+module.exports = webpackConfig;
diff --git a/tools/winscope/webpack.config.dev.js b/tools/winscope/webpack.config.dev.js
new file mode 100644
index 0000000..31d79ca
--- /dev/null
+++ b/tools/winscope/webpack.config.dev.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const webpack = require('webpack');
+const { merge } = require('webpack-merge');
+const path = require('path');
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
+const commonConfig = require('./webpack.config.common');
+const environment = require('./env/dev.env');
+
+const webpackConfig = merge(commonConfig, {
+  mode: 'development',
+  devtool: 'cheap-module-eval-source-map',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    publicPath: '/',
+    filename: 'js/[name].bundle.js',
+    chunkFilename: 'js/[id].chunk.js'
+  },
+  optimization: {
+    runtimeChunk: 'single',
+    splitChunks: {
+      chunks: 'all'
+    }
+  },
+  module: {
+    rules: [
+      // Enable sourcemaps for Kotlin code, source-map-loader should be configured
+      {
+        test: /\.js$/,
+        include: path.resolve(__dirname, './kotlin_build'),
+        exclude: [
+          /kotlin\.js$/, // Kotlin runtime doesn't have sourcemaps at the moment
+        ],
+        use: ['source-map-loader'],
+        enforce: 'pre'
+      },
+    ]
+  },
+  plugins: [
+    new webpack.EnvironmentPlugin(environment),
+    new webpack.HotModuleReplacementPlugin(),
+    new FriendlyErrorsPlugin()
+  ],
+  devServer: {
+    compress: true,
+    historyApiFallback: true,
+    hot: true,
+    open: true,
+    overlay: true,
+    port: 8080,
+    stats: {
+      normal: true
+    }
+  }
+});
+
+module.exports = webpackConfig;
\ No newline at end of file
diff --git a/tools/winscope/webpack.config.js b/tools/winscope/webpack.config.js
index ff31ff3..ac6ab23 100644
--- a/tools/winscope/webpack.config.js
+++ b/tools/winscope/webpack.config.js
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017, The Android Open Source Project
+ * Copyright 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,113 +14,12 @@
  * limitations under the License.
  */
 
-const fs = require('fs');
-var path = require('path')
-var webpack = require('webpack')
-var HtmlWebpackPlugin = require('html-webpack-plugin')
-var HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin')
+'use strict';
 
-function getWaylandSafePath() {
-  waylandPath = path.resolve(__dirname, '../../../vendor/google_arc/libs/wayland_service');
-  if (fs.existsSync(waylandPath)) {
-    return waylandPath;
-  }
-  return path.resolve(__dirname, 'src/stubs');
+const environment = (process.env.NODE_ENV || 'development').trim();
+
+if (environment === 'development') {
+  module.exports = require('./webpack.config.dev');
+} else {
+  module.exports = require('./webpack.config.prod');
 }
-
-module.exports = {
-  entry: './src/main.js',
-  output: {
-    path: path.resolve(__dirname, './dist'),
-    filename: 'build.js'
-  },
-  module: {
-    rules: [
-      {
-        test: /\.vue$/,
-        loader: 'vue-loader',
-        options: {
-          loaders: {
-          }
-          // other vue-loader options go here
-        }
-      },
-      {
-        test: /\.js$/,
-        loader: 'babel-loader',
-        exclude: /node_modules/
-      },
-      {
-        test: /\.proto$/,
-        loader: 'proto-loader',
-        options: {
-          paths: [
-            path.resolve(__dirname, '../../..'),
-            path.resolve(__dirname, '../../../external/protobuf/src')
-          ]
-        }
-      },
-      {
-        test: /\.(png|jpg|gif|svg)$/,
-        loader: 'file-loader',
-        options: {
-          name: '[name].[ext]?[hash]'
-        }
-      }
-    ]
-  },
-  resolve: {
-    alias: {
-        WaylandSafePath: getWaylandSafePath(),
-    },
-    modules: [
-      'node_modules',
-      path.resolve(__dirname, '../../..')
-    ],
-  },
-  resolveLoader: {
-    modules: [
-      'node_modules',
-      path.resolve(__dirname, 'loaders')
-    ]
-  },
-  devServer: {
-    historyApiFallback: true,
-    noInfo: true
-  },
-  performance: {
-    hints: false
-  },
-  devtool: '#eval-source-map'
-}
-
-var prod = (process.env.NODE_ENV === 'production');
-
-if (prod) {
-  module.exports.devtool = '#source-map'
-  // http://vue-loader.vuejs.org/en/workflow/production.html
-  module.exports.plugins = (module.exports.plugins || []).concat([
-    new webpack.DefinePlugin({
-      'process.env': {
-        NODE_ENV: '"production"'
-      }
-    }),
-    new webpack.optimize.UglifyJsPlugin({
-      sourceMap: true,
-      compress: {
-        warnings: false
-      }
-    }),
-    new webpack.LoaderOptionsPlugin({
-      minimize: true
-    })
-  ])
-}
-
-module.exports.plugins = (module.exports.plugins || []).concat([
-  new HtmlWebpackPlugin({
-    inlineSource: prod ? '.(js|css)' : false,
-    template: 'src/index_template.html'
-  }),
-  new HtmlWebpackInlineSourcePlugin(),
-]);
diff --git a/tools/winscope/webpack.config.prod.js b/tools/winscope/webpack.config.prod.js
new file mode 100644
index 0000000..33f7a8d
--- /dev/null
+++ b/tools/winscope/webpack.config.prod.js
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const webpack = require('webpack');
+const {merge} = require('webpack-merge');
+const path = require('path');
+const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
+const MiniCSSExtractPlugin = require('mini-css-extract-plugin');
+const CompressionPlugin = require('compression-webpack-plugin');
+const commonConfig = require('./webpack.config.common');
+const isProd = process.env.NODE_ENV === 'production';
+const environment =
+  isProd ? require('./env/prod.env') : require('./env/staging.env');
+
+
+const webpackConfig = merge(commonConfig, {
+  mode: 'production',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    publicPath: '/',
+    filename: 'js/[hash].js',
+    chunkFilename: 'js/[id].[hash].chunk.js',
+  },
+  optimization: {
+    runtimeChunk: 'single',
+    minimizer: [
+      new OptimizeCSSAssetsPlugin({
+        cssProcessorPluginOptions: {
+          preset: ['default', {discardComments: {removeAll: true}}],
+        },
+      }),
+    ],
+    splitChunks: {
+      chunks: 'all',
+      maxInitialRequests: Infinity,
+      minSize: 0,
+      cacheGroups: {
+        vendor: {
+          test: /[\\/]node_modules[\\/]/,
+          name(module) {
+            const packageName = module.context
+                .match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
+            return `npm.${packageName.replace('@', '')}`;
+          },
+        },
+        styles: {
+          test: /\.css$/,
+          name: 'styles',
+          chunks: 'all',
+          enforce: true,
+        },
+      },
+    },
+  },
+  plugins: [
+    new webpack.EnvironmentPlugin(environment),
+    new MiniCSSExtractPlugin({
+      filename: 'css/[name].[hash].css',
+      chunkFilename: 'css/[id].[hash].css',
+    }),
+    new CompressionPlugin({
+      filename: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp('\\.(js|css)$'),
+      threshold: 10240,
+      minRatio: 0.8,
+    }),
+    new webpack.HashedModuleIdsPlugin(),
+  ],
+});
+
+if (!isProd) {
+  webpackConfig.devtool = 'source-map';
+
+  if (process.env.npm_config_report) {
+    const BundleAnalyzerPlugin =
+      require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+    webpackConfig.plugins.push(new BundleAnalyzerPlugin());
+  }
+}
+
+module.exports = webpackConfig;
diff --git a/tools/winscope/webpack.spec.config.js b/tools/winscope/webpack.spec.config.js
new file mode 100644
index 0000000..edb2ab4
--- /dev/null
+++ b/tools/winscope/webpack.spec.config.js
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const glob = require('glob');
+const KotlinWebpackPlugin = require('@jetbrains/kotlin-webpack-plugin');
+
+function getWaylandSafePath() {
+  waylandPath = path.resolve(
+      __dirname, '../../../vendor/google_arc/libs/wayland_service');
+  if (fs.existsSync(waylandPath)) {
+    return waylandPath;
+  }
+  return path.resolve(__dirname, 'src/stubs');
+}
+
+module.exports = {
+  entry: {
+    js: glob.sync('./spec/**/*Spec.js'),
+  },
+  output: {
+    path: path.resolve(__dirname, './dist'),
+    filename: 'bundleSpec.js',
+  },
+  target: 'node',
+  node: {
+    __dirname: false,
+  },
+  module: {
+    rules: [
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.tsx?$/,
+        use: 'ts-loader',
+        include: path.resolve(__dirname, './src'),
+        exclude: /node_modules/,
+      },
+      {
+        test: /\.proto$/,
+        loader: 'proto-loader',
+        options: {
+          paths: [
+            path.resolve(__dirname, '../../..'),
+            path.resolve(__dirname, '../../../external/protobuf/src'),
+          ],
+        },
+      },
+      {
+        test: /\.pb/,
+        loader: 'file-loader',
+        options: {
+          paths: [
+            path.resolve(__dirname, './spec'),
+          ],
+        },
+      },
+    ],
+  },
+  resolve: {
+    extensions: ['.tsx', '.ts', '.js', '.vue'],
+    alias: {
+      '@': path.resolve(__dirname, 'src'),
+      'WaylandSafePath': getWaylandSafePath(),
+    },
+    modules: [
+      'node_modules',
+      'kotlin_build',
+      path.resolve(__dirname, '../../..'),
+    ],
+  },
+  resolveLoader: {
+    modules: [
+      'node_modules',
+      path.resolve(__dirname, 'loaders'),
+    ],
+  },
+  plugins: [
+    new KotlinWebpackPlugin({
+      src: [
+        path.join(__dirname, '../../../platform_testing/libraries/flicker/' +
+          'src/com/android/server/wm/flicker/common/'),
+      ],
+      output: 'kotlin_build',
+      moduleName: 'flicker',
+      librariesAutoLookup: true,
+      sourceMaps: true,
+      sourceMapEmbedSources: 'always',
+      verbose: true,
+      optimize: true,
+    }),
+  ],
+  devServer: {
+    historyApiFallback: true,
+    noInfo: true,
+  },
+  performance: {
+    hints: false,
+  },
+};
diff --git a/tools/winscope/yarn.lock b/tools/winscope/yarn.lock
index b91e14d..6badcac 100644
--- a/tools/winscope/yarn.lock
+++ b/tools/winscope/yarn.lock
@@ -2,6 +2,957 @@
 # yarn lockfile v1
 
 
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
+  integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
+  dependencies:
+    "@babel/highlight" "^7.10.4"
+
+"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c"
+  integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==
+  dependencies:
+    browserslist "^4.12.0"
+    invariant "^2.2.4"
+    semver "^5.5.0"
+
+"@babel/core@^7.10.5":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.5.tgz#6ad96e2f71899ea3f9b651f0a911e85205d1ff6d"
+  integrity sha512-fsEANVOcZHzrsV6dMVWqpSeXClq3lNbYrfFGme6DE25FQWe7pyeYpXyx9guqUnpy466JLzZ8z4uwSr2iv60V5Q==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    "@babel/generator" "^7.11.5"
+    "@babel/helper-module-transforms" "^7.11.0"
+    "@babel/helpers" "^7.10.4"
+    "@babel/parser" "^7.11.5"
+    "@babel/template" "^7.10.4"
+    "@babel/traverse" "^7.11.5"
+    "@babel/types" "^7.11.5"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.1"
+    json5 "^2.1.2"
+    lodash "^4.17.19"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.6.1"
+
+"@babel/generator@^7.11.5":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.5.tgz#a5582773425a468e4ba269d9a1f701fbca6a7a82"
+  integrity sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==
+  dependencies:
+    "@babel/types" "^7.11.5"
+    jsesc "^2.5.1"
+    source-map "^0.6.1"
+
+"@babel/helper-annotate-as-pure@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
+  integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3"
+  integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-compilation-targets@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2"
+  integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==
+  dependencies:
+    "@babel/compat-data" "^7.10.4"
+    browserslist "^4.12.0"
+    invariant "^2.2.4"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/helper-create-class-features-plugin@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
+  integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/helper-member-expression-to-functions" "^7.10.5"
+    "@babel/helper-optimise-call-expression" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-split-export-declaration" "^7.10.4"
+
+"@babel/helper-create-regexp-features-plugin@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8"
+  integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.4"
+    "@babel/helper-regex" "^7.10.4"
+    regexpu-core "^4.7.0"
+
+"@babel/helper-define-map@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30"
+  integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/types" "^7.10.5"
+    lodash "^4.17.19"
+
+"@babel/helper-explode-assignable-expression@^7.10.4":
+  version "7.11.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b"
+  integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-function-name@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
+  integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.10.4"
+    "@babel/template" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-get-function-arity@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
+  integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-hoist-variables@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e"
+  integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
+  integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+  dependencies:
+    "@babel/types" "^7.11.0"
+
+"@babel/helper-module-imports@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
+  integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
+  integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==
+  dependencies:
+    "@babel/helper-module-imports" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-simple-access" "^7.10.4"
+    "@babel/helper-split-export-declaration" "^7.11.0"
+    "@babel/template" "^7.10.4"
+    "@babel/types" "^7.11.0"
+    lodash "^4.17.19"
+
+"@babel/helper-optimise-call-expression@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
+  integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+  dependencies:
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
+  integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
+
+"@babel/helper-regex@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
+  integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==
+  dependencies:
+    lodash "^4.17.19"
+
+"@babel/helper-remap-async-to-generator@^7.10.4":
+  version "7.11.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d"
+  integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.4"
+    "@babel/helper-wrap-function" "^7.10.4"
+    "@babel/template" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-replace-supers@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
+  integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.10.4"
+    "@babel/helper-optimise-call-expression" "^7.10.4"
+    "@babel/traverse" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-simple-access@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
+  integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==
+  dependencies:
+    "@babel/template" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729"
+  integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==
+  dependencies:
+    "@babel/types" "^7.11.0"
+
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
+  integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
+  dependencies:
+    "@babel/types" "^7.11.0"
+
+"@babel/helper-validator-identifier@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
+  integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+
+"@babel/helper-wrap-function@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
+  integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/template" "^7.10.4"
+    "@babel/traverse" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/helpers@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
+  integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
+  dependencies:
+    "@babel/template" "^7.10.4"
+    "@babel/traverse" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/highlight@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
+  integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.10.4"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.10.4", "@babel/parser@^7.11.5":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
+  integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+
+"@babel/plugin-proposal-async-generator-functions@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558"
+  integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-remap-async-to-generator" "^7.10.4"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+
+"@babel/plugin-proposal-class-properties@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807"
+  integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-proposal-dynamic-import@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e"
+  integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+
+"@babel/plugin-proposal-export-namespace-from@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54"
+  integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-proposal-json-strings@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db"
+  integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+
+"@babel/plugin-proposal-logical-assignment-operators@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8"
+  integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a"
+  integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-numeric-separator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06"
+  integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af"
+  integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-transform-parameters" "^7.10.4"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd"
+  integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076"
+  integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-proposal-private-methods@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909"
+  integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.10.4", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d"
+  integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-async-generators@^7.8.0":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c"
+  integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+  integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+  integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-json-strings@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+  integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+  integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d"
+  integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-arrow-functions@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd"
+  integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-async-to-generator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37"
+  integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-remap-async-to-generator" "^7.10.4"
+
+"@babel/plugin-transform-block-scoped-functions@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8"
+  integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-block-scoping@^7.10.4":
+  version "7.11.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215"
+  integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-classes@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7"
+  integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.4"
+    "@babel/helper-define-map" "^7.10.4"
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/helper-optimise-call-expression" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.10.4"
+    "@babel/helper-split-export-declaration" "^7.10.4"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb"
+  integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-destructuring@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5"
+  integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee"
+  integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-duplicate-keys@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47"
+  integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-exponentiation-operator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e"
+  integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-for-of@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9"
+  integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-function-name@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7"
+  integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==
+  dependencies:
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-literals@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c"
+  integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-member-expression-literals@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7"
+  integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-modules-amd@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1"
+  integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.5"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-commonjs@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0"
+  integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-simple-access" "^7.10.4"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-systemjs@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85"
+  integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.10.4"
+    "@babel/helper-module-transforms" "^7.10.5"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-umd@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e"
+  integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6"
+  integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+
+"@babel/plugin-transform-new-target@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888"
+  integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-object-super@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894"
+  integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-replace-supers" "^7.10.4"
+
+"@babel/plugin-transform-parameters@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a"
+  integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-property-literals@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0"
+  integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-regenerator@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
+  integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==
+  dependencies:
+    regenerator-transform "^0.14.2"
+
+"@babel/plugin-transform-reserved-words@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd"
+  integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-shorthand-properties@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6"
+  integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-spread@^7.11.0":
+  version "7.11.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc"
+  integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
+
+"@babel/plugin-transform-sticky-regex@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d"
+  integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/helper-regex" "^7.10.4"
+
+"@babel/plugin-transform-template-literals@^7.10.4":
+  version "7.10.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c"
+  integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-typeof-symbol@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc"
+  integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-unicode-escapes@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007"
+  integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-transform-unicode-regex@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8"
+  integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/polyfill@^7.10.4":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.11.5.tgz#df550b2ec53abbc2ed599367ec59e64c7a707bb5"
+  integrity sha512-FunXnE0Sgpd61pKSj2OSOs1D44rKTD3pGOfGilZ6LGrrIH0QEtJlTjqOqdF8Bs98JmjfGhni2BBkTfv9KcKJ9g==
+  dependencies:
+    core-js "^2.6.5"
+    regenerator-runtime "^0.13.4"
+
+"@babel/preset-env@^7.10.4":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272"
+  integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==
+  dependencies:
+    "@babel/compat-data" "^7.11.0"
+    "@babel/helper-compilation-targets" "^7.10.4"
+    "@babel/helper-module-imports" "^7.10.4"
+    "@babel/helper-plugin-utils" "^7.10.4"
+    "@babel/plugin-proposal-async-generator-functions" "^7.10.4"
+    "@babel/plugin-proposal-class-properties" "^7.10.4"
+    "@babel/plugin-proposal-dynamic-import" "^7.10.4"
+    "@babel/plugin-proposal-export-namespace-from" "^7.10.4"
+    "@babel/plugin-proposal-json-strings" "^7.10.4"
+    "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0"
+    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4"
+    "@babel/plugin-proposal-numeric-separator" "^7.10.4"
+    "@babel/plugin-proposal-object-rest-spread" "^7.11.0"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.10.4"
+    "@babel/plugin-proposal-optional-chaining" "^7.11.0"
+    "@babel/plugin-proposal-private-methods" "^7.10.4"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.10.4"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+    "@babel/plugin-syntax-class-properties" "^7.10.4"
+    "@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.10.4"
+    "@babel/plugin-transform-arrow-functions" "^7.10.4"
+    "@babel/plugin-transform-async-to-generator" "^7.10.4"
+    "@babel/plugin-transform-block-scoped-functions" "^7.10.4"
+    "@babel/plugin-transform-block-scoping" "^7.10.4"
+    "@babel/plugin-transform-classes" "^7.10.4"
+    "@babel/plugin-transform-computed-properties" "^7.10.4"
+    "@babel/plugin-transform-destructuring" "^7.10.4"
+    "@babel/plugin-transform-dotall-regex" "^7.10.4"
+    "@babel/plugin-transform-duplicate-keys" "^7.10.4"
+    "@babel/plugin-transform-exponentiation-operator" "^7.10.4"
+    "@babel/plugin-transform-for-of" "^7.10.4"
+    "@babel/plugin-transform-function-name" "^7.10.4"
+    "@babel/plugin-transform-literals" "^7.10.4"
+    "@babel/plugin-transform-member-expression-literals" "^7.10.4"
+    "@babel/plugin-transform-modules-amd" "^7.10.4"
+    "@babel/plugin-transform-modules-commonjs" "^7.10.4"
+    "@babel/plugin-transform-modules-systemjs" "^7.10.4"
+    "@babel/plugin-transform-modules-umd" "^7.10.4"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4"
+    "@babel/plugin-transform-new-target" "^7.10.4"
+    "@babel/plugin-transform-object-super" "^7.10.4"
+    "@babel/plugin-transform-parameters" "^7.10.4"
+    "@babel/plugin-transform-property-literals" "^7.10.4"
+    "@babel/plugin-transform-regenerator" "^7.10.4"
+    "@babel/plugin-transform-reserved-words" "^7.10.4"
+    "@babel/plugin-transform-shorthand-properties" "^7.10.4"
+    "@babel/plugin-transform-spread" "^7.11.0"
+    "@babel/plugin-transform-sticky-regex" "^7.10.4"
+    "@babel/plugin-transform-template-literals" "^7.10.4"
+    "@babel/plugin-transform-typeof-symbol" "^7.10.4"
+    "@babel/plugin-transform-unicode-escapes" "^7.10.4"
+    "@babel/plugin-transform-unicode-regex" "^7.10.4"
+    "@babel/preset-modules" "^0.1.3"
+    "@babel/types" "^7.11.5"
+    browserslist "^4.12.0"
+    core-js-compat "^3.6.2"
+    invariant "^2.2.2"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/preset-modules@^0.1.3":
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e"
+  integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+    "@babel/plugin-transform-dotall-regex" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    esutils "^2.0.2"
+
+"@babel/register@^7.10.5":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.11.5.tgz#79becf89e0ddd0fba8b92bc279bc0f5d2d7ce2ea"
+  integrity sha512-CAml0ioKX+kOAvBQDHa/+t1fgOt3qkTIz0TrRtRAT6XY0m5qYZXR85k6/sLCNPMGhYDlCFHCYuU0ybTJbvlC6w==
+  dependencies:
+    find-cache-dir "^2.0.0"
+    lodash "^4.17.19"
+    make-dir "^2.1.0"
+    pirates "^4.0.0"
+    source-map-support "^0.5.16"
+
+"@babel/runtime-corejs3@^7.10.2":
+  version "7.11.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz#02c3029743150188edeb66541195f54600278419"
+  integrity sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==
+  dependencies:
+    core-js-pure "^3.0.0"
+    regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.8.4":
+  version "7.11.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
+  integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.10.4":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
+  integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    "@babel/parser" "^7.10.4"
+    "@babel/types" "^7.10.4"
+
+"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
+  integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    "@babel/generator" "^7.11.5"
+    "@babel/helper-function-name" "^7.10.4"
+    "@babel/helper-split-export-declaration" "^7.11.0"
+    "@babel/parser" "^7.11.5"
+    "@babel/types" "^7.11.5"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.19"
+
+"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.4.4":
+  version "7.11.5"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
+  integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.10.4"
+    lodash "^4.17.19"
+    to-fast-properties "^2.0.0"
+
+"@eslint/eslintrc@^0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085"
+  integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.1.1"
+    espree "^7.3.0"
+    globals "^12.1.0"
+    ignore "^4.0.6"
+    import-fresh "^3.2.1"
+    js-yaml "^3.13.1"
+    lodash "^4.17.19"
+    minimatch "^3.0.4"
+    strip-json-comments "^3.1.1"
+
+"@jest/types@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d"
+  integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==
+  dependencies:
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^1.1.1"
+    "@types/yargs" "^15.0.0"
+    chalk "^3.0.0"
+
+"@jest/types@^26.3.0":
+  version "26.3.0"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71"
+  integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==
+  dependencies:
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^3.0.0"
+    "@types/node" "*"
+    "@types/yargs" "^15.0.0"
+    chalk "^4.0.0"
+
+"@jetbrains/kotlin-webpack-plugin@^3.0.2":
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/@jetbrains/kotlin-webpack-plugin/-/kotlin-webpack-plugin-3.0.2.tgz#a8c0c5dfc35cfc4d72faf8c1921bddca46849e20"
+  integrity sha512-I3vYv6RXRHSQpmMeClVISdo0KDGeF+FJ0v+p84DCJ3eJzBWbHtYbCwMg5rEHlX05D793RnKa4f5dyyOQOixLPA==
+  dependencies:
+    "@jetbrains/kotlinc-js-api" "^2.0.1"
+    fs-extra "^8.1.0"
+    glob "^7.1.6"
+    globby "^10.0.1"
+    kotlin-compiler "^1.3.61"
+    webpack-log "^3.0.1"
+
+"@jetbrains/kotlinc-js-api@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@jetbrains/kotlinc-js-api/-/kotlinc-js-api-2.0.1.tgz#17c3211fd7eccf3e9a6bd3bf3ec45937d94a45c1"
+  integrity sha512-l4gvsbZjscFu0yNlrnQqKoYmOoVHHmZ6q5AyRpXQk+lPWKvbK73qnuFT9oihUGGqRrDqPZ6hQO73exYbz4O+zw==
+  dependencies:
+    cross-spawn "^7.0.1"
+    kotlin-compiler "^1.3.61"
+
+"@mrmlnc/readdir-enhanced@^2.2.1":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
+  integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
+  dependencies:
+    call-me-maybe "^1.0.1"
+    glob-to-regexp "^0.3.0"
+
+"@nodelib/fs.scandir@2.1.3":
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
+  integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.3"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
+  integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
+
+"@nodelib/fs.stat@^1.1.2":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
+  integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
+
+"@nodelib/fs.walk@^1.2.3":
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
+  integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.3"
+    fastq "^1.6.0"
+
+"@npmcli/move-file@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
+  integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==
+  dependencies:
+    mkdirp "^1.0.4"
+
 "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@@ -55,15 +1006,322 @@
   resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
   integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
 
-"@types/long@^4.0.0":
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef"
-  integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==
+"@testing-library/dom@^7.24.3", "@testing-library/dom@^7.5.7":
+  version "7.24.3"
+  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.24.3.tgz#dae3071463cf28dc7755b43d9cf2202e34cbb85d"
+  integrity sha512-6eW9fUhEbR423FZvoHRwbWm9RUUByLWGayYFNVvqTnQLYvsNpBS4uEuKH9aqr3trhxFwGVneJUonehL3B1sHJw==
+  dependencies:
+    "@babel/code-frame" "^7.10.4"
+    "@babel/runtime" "^7.10.3"
+    "@types/aria-query" "^4.2.0"
+    aria-query "^4.2.2"
+    chalk "^4.1.0"
+    dom-accessibility-api "^0.5.1"
+    pretty-format "^26.4.2"
 
-"@types/node@^10.1.0":
-  version "10.14.13"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.13.tgz#ac786d623860adf39a3f51d629480aacd6a6eec7"
-  integrity sha512-yN/FNNW1UYsRR1wwAoyOwqvDuLDtVXnaJTZ898XIw/Q5cCaeVAlVwvsmXLX5PuiScBYwZsZU4JYSHB3TvfdwvQ==
+"@testing-library/vue@^5.1.0":
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/vue/-/vue-5.1.0.tgz#3d0eb3d1861661c44f2bc20f6a1b5d8bd8b7500c"
+  integrity sha512-RuV63Ywys7rhF+UpdSKpFrcQfyiGj9ecxAL76HCOCGbtlXdyqUbGegZ+vZpC22scTCSxmr42l0g0gJEyp00W+g==
+  dependencies:
+    "@babel/runtime" "^7.11.2"
+    "@testing-library/dom" "^7.24.3"
+    "@types/testing-library__vue" "^5.0.0"
+    "@vue/test-utils" "^1.1.0"
+
+"@types/aria-query@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
+  integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==
+
+"@types/color-name@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
+  integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+
+"@types/glob@^7.1.1":
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
+  integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
+  dependencies:
+    "@types/minimatch" "*"
+    "@types/node" "*"
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
+  integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==
+
+"@types/istanbul-lib-report@*":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
+  integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^1.1.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2"
+  integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+    "@types/istanbul-lib-report" "*"
+
+"@types/istanbul-reports@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821"
+  integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==
+  dependencies:
+    "@types/istanbul-lib-report" "*"
+
+"@types/json-schema@^7.0.5":
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
+  integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
+
+"@types/lodash@^4.14.158":
+  version "4.14.158"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.158.tgz#b38ea8b6fe799acd076d7a8d7ab71c26ef77f785"
+  integrity sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w==
+
+"@types/long@^4.0.1":
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
+  integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
+
+"@types/minimatch@*":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+  integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*":
+  version "14.6.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.2.tgz#264b44c5a28dfa80198fc2f7b6d3c8a054b9491f"
+  integrity sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==
+
+"@types/node@^13.7.0":
+  version "13.13.15"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.15.tgz#fe1cc3aa465a3ea6858b793fd380b66c39919766"
+  integrity sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw==
+
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+"@types/q@^1.5.1":
+  version "1.5.4"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
+  integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
+
+"@types/testing-library__vue@^5.0.0":
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/@types/testing-library__vue/-/testing-library__vue-5.0.0.tgz#b16de69eb2769f6c9bb85c66a1dab53ca56ee1fd"
+  integrity sha512-R94Vv9UfDoW/Vvb+ZBsy9evZO7utmFhEoe4F7+Xo8G/DpO5TZM/BdL07zkF3sjqWSyRhbHYHwXIrPwJAXQZGHg==
+  dependencies:
+    "@testing-library/dom" "^7.5.7"
+    "@vue/test-utils" "^1.0.3"
+    pretty-format "^25.5.0"
+    vue "^2.6.10"
+    vue-router "^3.0"
+    vuex "^3.0"
+
+"@types/yargs-parser@*":
+  version "15.0.0"
+  resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
+  integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
+
+"@types/yargs@^15.0.0":
+  version "15.0.7"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.7.tgz#dad50a7a234a35ef9460737a56024287a3de1d2b"
+  integrity sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==
+  dependencies:
+    "@types/yargs-parser" "*"
+
+"@vue/component-compiler-utils@^3.1.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz#8f85182ceed28e9b3c75313de669f83166d11e5d"
+  integrity sha512-lejBLa7xAMsfiZfNp7Kv51zOzifnb29FwdnMLa96z26kXErPFioSf9BMcePVIQ6/Gc6/mC0UrPpxAWIHyae0vw==
+  dependencies:
+    consolidate "^0.15.1"
+    hash-sum "^1.0.2"
+    lru-cache "^4.1.2"
+    merge-source-map "^1.1.0"
+    postcss "^7.0.14"
+    postcss-selector-parser "^6.0.2"
+    source-map "~0.6.1"
+    vue-template-es2015-compiler "^1.9.0"
+  optionalDependencies:
+    prettier "^1.18.2"
+
+"@vue/test-utils@^1.0.3", "@vue/test-utils@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.1.0.tgz#76305e73a786c921ede1352849614e26c7113f94"
+  integrity sha512-M+3jtVqNYIrvzO5gaxogre5a5+96h0hN/dXw+5Lj0t+dp6fAhYcUjpLrC9j9cEEkl2Rcuh/gKYRUmR5N4vcqPw==
+  dependencies:
+    dom-event-types "^1.0.0"
+    lodash "^4.17.15"
+    pretty "^2.0.0"
+
+"@webassemblyjs/ast@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
+  integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==
+  dependencies:
+    "@webassemblyjs/helper-module-context" "1.9.0"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
+    "@webassemblyjs/wast-parser" "1.9.0"
+
+"@webassemblyjs/floating-point-hex-parser@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4"
+  integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==
+
+"@webassemblyjs/helper-api-error@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2"
+  integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==
+
+"@webassemblyjs/helper-buffer@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00"
+  integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==
+
+"@webassemblyjs/helper-code-frame@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27"
+  integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.9.0"
+
+"@webassemblyjs/helper-fsm@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8"
+  integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==
+
+"@webassemblyjs/helper-module-context@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07"
+  integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+
+"@webassemblyjs/helper-wasm-bytecode@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790"
+  integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==
+
+"@webassemblyjs/helper-wasm-section@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346"
+  integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-buffer" "1.9.0"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
+    "@webassemblyjs/wasm-gen" "1.9.0"
+
+"@webassemblyjs/ieee754@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4"
+  integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==
+  dependencies:
+    "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95"
+  integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==
+  dependencies:
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab"
+  integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==
+
+"@webassemblyjs/wasm-edit@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf"
+  integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-buffer" "1.9.0"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
+    "@webassemblyjs/helper-wasm-section" "1.9.0"
+    "@webassemblyjs/wasm-gen" "1.9.0"
+    "@webassemblyjs/wasm-opt" "1.9.0"
+    "@webassemblyjs/wasm-parser" "1.9.0"
+    "@webassemblyjs/wast-printer" "1.9.0"
+
+"@webassemblyjs/wasm-gen@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c"
+  integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
+    "@webassemblyjs/ieee754" "1.9.0"
+    "@webassemblyjs/leb128" "1.9.0"
+    "@webassemblyjs/utf8" "1.9.0"
+
+"@webassemblyjs/wasm-opt@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61"
+  integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-buffer" "1.9.0"
+    "@webassemblyjs/wasm-gen" "1.9.0"
+    "@webassemblyjs/wasm-parser" "1.9.0"
+
+"@webassemblyjs/wasm-parser@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e"
+  integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-api-error" "1.9.0"
+    "@webassemblyjs/helper-wasm-bytecode" "1.9.0"
+    "@webassemblyjs/ieee754" "1.9.0"
+    "@webassemblyjs/leb128" "1.9.0"
+    "@webassemblyjs/utf8" "1.9.0"
+
+"@webassemblyjs/wast-parser@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914"
+  integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/floating-point-hex-parser" "1.9.0"
+    "@webassemblyjs/helper-api-error" "1.9.0"
+    "@webassemblyjs/helper-code-frame" "1.9.0"
+    "@webassemblyjs/helper-fsm" "1.9.0"
+    "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/wast-printer@1.9.0":
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899"
+  integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==
+  dependencies:
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/wast-parser" "1.9.0"
+    "@xtuc/long" "4.2.2"
+
+"@xtuc/ieee754@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+  integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+  integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+abab@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.4.tgz#6dfa57b417ca06d21b2478f0e638302f99c2405c"
+  integrity sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ==
 
 abbrev@1:
   version "1.1.1"
@@ -78,60 +1336,71 @@
     mime-types "~2.1.24"
     negotiator "0.6.2"
 
-acorn-dynamic-import@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
-  integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=
+acorn-jsx@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
+  integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
+
+acorn@^6.4.1:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
+  integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
+
+acorn@^7.1.1, acorn@^7.4.0:
+  version "7.4.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c"
+  integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==
+
+aggregate-error@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+  integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
   dependencies:
-    acorn "^4.0.3"
+    clean-stack "^2.0.0"
+    indent-string "^4.0.0"
 
-acorn@^4.0.3:
-  version "4.0.13"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
-  integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=
+ajv-errors@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
+  integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
 
-acorn@^5.0.0:
-  version "5.7.3"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
-  integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
+ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+  integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
 
-ajv-keywords@^1.1.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
-  integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw=
-
-ajv@^4.7.0:
-  version "4.11.8"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
-  integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4:
+  version "6.12.4"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
+  integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==
   dependencies:
-    co "^4.6.0"
-    json-stable-stringify "^1.0.1"
-
-ajv@^5.0.0:
-  version "5.5.2"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
-  integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
-  dependencies:
-    co "^4.6.0"
-    fast-deep-equal "^1.0.0"
+    fast-deep-equal "^3.1.1"
     fast-json-stable-stringify "^2.0.0"
-    json-schema-traverse "^0.3.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
 
-align-text@^0.1.1, align-text@^0.1.3:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
-  integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=
-  dependencies:
-    kind-of "^3.0.2"
-    longest "^1.0.1"
-    repeat-string "^1.5.2"
-
-alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
+alphanum-sort@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
   integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
 
+ansi-colors@^3.0.0:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+  integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
+
+ansi-colors@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
+  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+
+ansi-escapes@^4.3.0:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
+  integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==
+  dependencies:
+    type-fest "^0.11.0"
+
 ansi-html@0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
@@ -147,18 +1416,36 @@
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
   integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
 
+ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-regex@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
+  integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
   integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
 
-ansi-styles@^3.2.1:
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
   integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
+  integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+  dependencies:
+    "@types/color-name" "^1.1.1"
+    color-convert "^2.0.1"
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -167,19 +1454,19 @@
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-aproba@^1.0.3:
+anymatch@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
+  integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+aproba@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
 
-are-we-there-yet@~1.1.2:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
-  integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
-  dependencies:
-    delegates "^1.0.0"
-    readable-stream "^2.0.6"
-
 argparse@^1.0.7:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -187,6 +1474,14 @@
   dependencies:
     sprintf-js "~1.0.2"
 
+aria-query@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
+  integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
+  dependencies:
+    "@babel/runtime" "^7.10.2"
+    "@babel/runtime-corejs3" "^7.10.2"
+
 arr-diff@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -202,11 +1497,6 @@
   resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
   integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
 
-array-find-index@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
-  integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
-
 array-flatten@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -217,14 +1507,6 @@
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
   integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
 
-array-includes@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
-  integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=
-  dependencies:
-    define-properties "^1.1.2"
-    es-abstract "^1.7.0"
-
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -232,6 +1514,11 @@
   dependencies:
     array-uniq "^1.0.1"
 
+array-union@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+  integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
 array-uniq@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@@ -242,14 +1529,15 @@
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
-asn1.js@^4.0.0:
-  version "4.10.1"
-  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
-  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+asn1.js@^5.2.0:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
+  integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
   dependencies:
     bn.js "^4.0.0"
     inherits "^2.0.1"
     minimalistic-assert "^1.0.0"
+    safer-buffer "^2.1.0"
 
 assert@^1.1.1:
   version "1.5.0"
@@ -264,565 +1552,55 @@
   resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
   integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
 
+astral-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+  integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
+astral-regex@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+  integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
 async-each@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
   integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
 
-async@^1.5.2:
-  version "1.5.2"
-  resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-  integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
+async-limiter@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+  integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
 
-async@^2.1.2:
+async@^2.6.2:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
   integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
   dependencies:
     lodash "^4.17.14"
 
-atob@^2.1.1:
+atob@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
   integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
 
-autoprefixer@^6.3.1:
-  version "6.7.7"
-  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
-  integrity sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=
+babel-loader@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3"
+  integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==
   dependencies:
-    browserslist "^1.7.6"
-    caniuse-db "^1.0.30000634"
-    normalize-range "^0.1.2"
-    num2fraction "^1.2.2"
-    postcss "^5.2.16"
-    postcss-value-parser "^3.2.3"
+    find-cache-dir "^2.1.0"
+    loader-utils "^1.4.0"
+    mkdirp "^0.5.3"
+    pify "^4.0.1"
+    schema-utils "^2.6.5"
 
-babel-code-frame@^6.11.0, babel-code-frame@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
-  integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
+babel-plugin-dynamic-import-node@^2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
+  integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==
   dependencies:
-    chalk "^1.1.3"
-    esutils "^2.0.2"
-    js-tokens "^3.0.2"
-
-babel-core@^6.0.0, babel-core@^6.26.0:
-  version "6.26.3"
-  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
-  integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==
-  dependencies:
-    babel-code-frame "^6.26.0"
-    babel-generator "^6.26.0"
-    babel-helpers "^6.24.1"
-    babel-messages "^6.23.0"
-    babel-register "^6.26.0"
-    babel-runtime "^6.26.0"
-    babel-template "^6.26.0"
-    babel-traverse "^6.26.0"
-    babel-types "^6.26.0"
-    babylon "^6.18.0"
-    convert-source-map "^1.5.1"
-    debug "^2.6.9"
-    json5 "^0.5.1"
-    lodash "^4.17.4"
-    minimatch "^3.0.4"
-    path-is-absolute "^1.0.1"
-    private "^0.1.8"
-    slash "^1.0.0"
-    source-map "^0.5.7"
-
-babel-generator@^6.26.0:
-  version "6.26.1"
-  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
-  integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==
-  dependencies:
-    babel-messages "^6.23.0"
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    detect-indent "^4.0.0"
-    jsesc "^1.3.0"
-    lodash "^4.17.4"
-    source-map "^0.5.7"
-    trim-right "^1.0.1"
-
-babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
-  integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=
-  dependencies:
-    babel-helper-explode-assignable-expression "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-call-delegate@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
-  integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=
-  dependencies:
-    babel-helper-hoist-variables "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-define-map@^6.24.1:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f"
-  integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=
-  dependencies:
-    babel-helper-function-name "^6.24.1"
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-helper-explode-assignable-expression@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa"
-  integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-function-name@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
-  integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=
-  dependencies:
-    babel-helper-get-function-arity "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-get-function-arity@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
-  integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-hoist-variables@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
-  integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-optimise-call-expression@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
-  integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-helper-regex@^6.24.1:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72"
-  integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=
-  dependencies:
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-helper-remap-async-to-generator@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b"
-  integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=
-  dependencies:
-    babel-helper-function-name "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helper-replace-supers@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
-  integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo=
-  dependencies:
-    babel-helper-optimise-call-expression "^6.24.1"
-    babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-helpers@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
-  integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-loader@^6.0.0:
-  version "6.4.1"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca"
-  integrity sha1-CzQRLVsHSKjc2/Uaz2+b1C1QuMo=
-  dependencies:
-    find-cache-dir "^0.1.1"
-    loader-utils "^0.2.16"
-    mkdirp "^0.5.1"
-    object-assign "^4.0.1"
-
-babel-messages@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
-  integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-check-es2015-constants@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
-  integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-syntax-async-functions@^6.8.0:
-  version "6.13.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
-  integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=
-
-babel-plugin-syntax-exponentiation-operator@^6.8.0:
-  version "6.13.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
-  integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=
-
-babel-plugin-syntax-trailing-function-commas@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
-  integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=
-
-babel-plugin-transform-async-to-generator@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
-  integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=
-  dependencies:
-    babel-helper-remap-async-to-generator "^6.24.1"
-    babel-plugin-syntax-async-functions "^6.8.0"
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-arrow-functions@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
-  integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
-  integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-block-scoping@^6.23.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f"
-  integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=
-  dependencies:
-    babel-runtime "^6.26.0"
-    babel-template "^6.26.0"
-    babel-traverse "^6.26.0"
-    babel-types "^6.26.0"
-    lodash "^4.17.4"
-
-babel-plugin-transform-es2015-classes@^6.23.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
-  integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=
-  dependencies:
-    babel-helper-define-map "^6.24.1"
-    babel-helper-function-name "^6.24.1"
-    babel-helper-optimise-call-expression "^6.24.1"
-    babel-helper-replace-supers "^6.24.1"
-    babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-computed-properties@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
-  integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-destructuring@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
-  integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-duplicate-keys@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
-  integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-for-of@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
-  integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-function-name@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
-  integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=
-  dependencies:
-    babel-helper-function-name "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-literals@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
-  integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
-  integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=
-  dependencies:
-    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
-  version "6.26.2"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3"
-  integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==
-  dependencies:
-    babel-plugin-transform-strict-mode "^6.24.1"
-    babel-runtime "^6.26.0"
-    babel-template "^6.26.0"
-    babel-types "^6.26.0"
-
-babel-plugin-transform-es2015-modules-systemjs@^6.23.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
-  integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=
-  dependencies:
-    babel-helper-hoist-variables "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-modules-umd@^6.23.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
-  integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg=
-  dependencies:
-    babel-plugin-transform-es2015-modules-amd "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-
-babel-plugin-transform-es2015-object-super@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
-  integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40=
-  dependencies:
-    babel-helper-replace-supers "^6.24.1"
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-parameters@^6.23.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
-  integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=
-  dependencies:
-    babel-helper-call-delegate "^6.24.1"
-    babel-helper-get-function-arity "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-template "^6.24.1"
-    babel-traverse "^6.24.1"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-shorthand-properties@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
-  integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-spread@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
-  integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-sticky-regex@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
-  integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw=
-  dependencies:
-    babel-helper-regex "^6.24.1"
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-plugin-transform-es2015-template-literals@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
-  integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-typeof-symbol@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
-  integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=
-  dependencies:
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-es2015-unicode-regex@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
-  integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek=
-  dependencies:
-    babel-helper-regex "^6.24.1"
-    babel-runtime "^6.22.0"
-    regexpu-core "^2.0.0"
-
-babel-plugin-transform-exponentiation-operator@^6.22.0:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
-  integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=
-  dependencies:
-    babel-helper-builder-binary-assignment-operator-visitor "^6.24.1"
-    babel-plugin-syntax-exponentiation-operator "^6.8.0"
-    babel-runtime "^6.22.0"
-
-babel-plugin-transform-regenerator@^6.22.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f"
-  integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=
-  dependencies:
-    regenerator-transform "^0.10.0"
-
-babel-plugin-transform-strict-mode@^6.24.1:
-  version "6.24.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
-  integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=
-  dependencies:
-    babel-runtime "^6.22.0"
-    babel-types "^6.24.1"
-
-babel-preset-env@^1.5.1:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
-  integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==
-  dependencies:
-    babel-plugin-check-es2015-constants "^6.22.0"
-    babel-plugin-syntax-trailing-function-commas "^6.22.0"
-    babel-plugin-transform-async-to-generator "^6.22.0"
-    babel-plugin-transform-es2015-arrow-functions "^6.22.0"
-    babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
-    babel-plugin-transform-es2015-block-scoping "^6.23.0"
-    babel-plugin-transform-es2015-classes "^6.23.0"
-    babel-plugin-transform-es2015-computed-properties "^6.22.0"
-    babel-plugin-transform-es2015-destructuring "^6.23.0"
-    babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
-    babel-plugin-transform-es2015-for-of "^6.23.0"
-    babel-plugin-transform-es2015-function-name "^6.22.0"
-    babel-plugin-transform-es2015-literals "^6.22.0"
-    babel-plugin-transform-es2015-modules-amd "^6.22.0"
-    babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
-    babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
-    babel-plugin-transform-es2015-modules-umd "^6.23.0"
-    babel-plugin-transform-es2015-object-super "^6.22.0"
-    babel-plugin-transform-es2015-parameters "^6.23.0"
-    babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
-    babel-plugin-transform-es2015-spread "^6.22.0"
-    babel-plugin-transform-es2015-sticky-regex "^6.22.0"
-    babel-plugin-transform-es2015-template-literals "^6.22.0"
-    babel-plugin-transform-es2015-typeof-symbol "^6.23.0"
-    babel-plugin-transform-es2015-unicode-regex "^6.22.0"
-    babel-plugin-transform-exponentiation-operator "^6.22.0"
-    babel-plugin-transform-regenerator "^6.22.0"
-    browserslist "^3.2.6"
-    invariant "^2.2.2"
-    semver "^5.3.0"
-
-babel-register@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
-  integrity sha1-btAhFz4vy0htestFxgCahW9kcHE=
-  dependencies:
-    babel-core "^6.26.0"
-    babel-runtime "^6.26.0"
-    core-js "^2.5.0"
-    home-or-tmp "^2.0.0"
-    lodash "^4.17.4"
-    mkdirp "^0.5.1"
-    source-map-support "^0.4.15"
-
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
-  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
-  dependencies:
-    core-js "^2.4.0"
-    regenerator-runtime "^0.11.0"
-
-babel-template@^6.24.1, babel-template@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
-  integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
-  dependencies:
-    babel-runtime "^6.26.0"
-    babel-traverse "^6.26.0"
-    babel-types "^6.26.0"
-    babylon "^6.18.0"
-    lodash "^4.17.4"
-
-babel-traverse@^6.24.1, babel-traverse@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
-  integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
-  dependencies:
-    babel-code-frame "^6.26.0"
-    babel-messages "^6.23.0"
-    babel-runtime "^6.26.0"
-    babel-types "^6.26.0"
-    babylon "^6.18.0"
-    debug "^2.6.8"
-    globals "^9.18.0"
-    invariant "^2.2.2"
-    lodash "^4.17.4"
-
-babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
-  version "6.26.0"
-  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
-  integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
-  dependencies:
-    babel-runtime "^6.26.0"
-    esutils "^2.0.2"
-    lodash "^4.17.4"
-    to-fast-properties "^1.0.3"
-
-babylon@^6.18.0:
-  version "6.18.0"
-  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
-  integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
-
-balanced-match@^0.4.2:
-  version "0.4.2"
-  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-  integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=
+    object.assign "^4.1.0"
 
 balanced-match@^1.0.0:
   version "1.0.0"
@@ -830,9 +1608,9 @@
   integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
 
 base64-js@^1.0.2:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
-  integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
 
 base@^0.11.1:
   version "0.11.2"
@@ -867,15 +1645,32 @@
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
   integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
 
-bluebird@^3.1.1, bluebird@^3.4.7:
-  version "3.5.5"
-  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
-  integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
+binary-extensions@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
+  integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
 
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
-  version "4.11.8"
-  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
-  integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+bindings@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+  integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+  dependencies:
+    file-uri-to-path "1.0.0"
+
+bluebird@^3.1.1, bluebird@^3.5.5:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
+  integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0:
+  version "4.11.9"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+  integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+
+bn.js@^5.1.1:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
+  integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
 
 body-parser@1.19.0:
   version "1.19.0"
@@ -905,7 +1700,7 @@
     multicast-dns "^6.0.1"
     multicast-dns-service-types "^1.1.0"
 
-boolbase@~1.0.0:
+boolbase@^1.0.0, boolbase@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
   integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@@ -934,6 +1729,13 @@
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
+braces@^3.0.1, braces@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
 brorand@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -970,7 +1772,7 @@
     inherits "^2.0.1"
     safe-buffer "^5.1.2"
 
-browserify-rsa@^4.0.0:
+browserify-rsa@^4.0.0, browserify-rsa@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
   integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
@@ -979,17 +1781,19 @@
     randombytes "^2.0.1"
 
 browserify-sign@^4.0.0:
-  version "4.0.4"
-  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
-  integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3"
+  integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==
   dependencies:
-    bn.js "^4.1.1"
-    browserify-rsa "^4.0.0"
-    create-hash "^1.1.0"
-    create-hmac "^1.1.2"
-    elliptic "^6.0.0"
-    inherits "^2.0.1"
-    parse-asn1 "^5.0.0"
+    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@^0.2.0:
   version "0.2.0"
@@ -998,21 +1802,20 @@
   dependencies:
     pako "~1.0.5"
 
-browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
-  version "1.7.7"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
-  integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=
+browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5:
+  version "4.14.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.0.tgz#2908951abfe4ec98737b72f34c3bcedc8d43b000"
+  integrity sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==
   dependencies:
-    caniuse-db "^1.0.30000639"
-    electron-to-chromium "^1.2.7"
+    caniuse-lite "^1.0.30001111"
+    electron-to-chromium "^1.3.523"
+    escalade "^3.0.2"
+    node-releases "^1.1.60"
 
-browserslist@^3.2.6:
-  version "3.2.8"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6"
-  integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==
-  dependencies:
-    caniuse-lite "^1.0.30000844"
-    electron-to-chromium "^1.3.47"
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
 buffer-indexof@^1.0.0:
   version "1.1.1"
@@ -1025,9 +1828,9 @@
   integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
 
 buffer@^4.3.0:
-  version "4.9.1"
-  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
-  integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
+  version "4.9.2"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+  integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
   dependencies:
     base64-js "^1.0.2"
     ieee754 "^1.1.4"
@@ -1048,6 +1851,50 @@
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
   integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
 
+cacache@^12.0.2:
+  version "12.0.4"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
+  integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==
+  dependencies:
+    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"
+
+cacache@^15.0.5:
+  version "15.0.5"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0"
+  integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==
+  dependencies:
+    "@npmcli/move-file" "^1.0.1"
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    glob "^7.1.4"
+    infer-owner "^1.0.4"
+    lru-cache "^6.0.0"
+    minipass "^3.1.1"
+    minipass-collect "^1.0.2"
+    minipass-flush "^1.0.5"
+    minipass-pipeline "^1.2.2"
+    mkdirp "^1.0.3"
+    p-map "^4.0.0"
+    promise-inflight "^1.0.1"
+    rimraf "^3.0.2"
+    ssri "^8.0.0"
+    tar "^6.0.2"
+    unique-filename "^1.1.1"
+
 cache-base@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -1063,6 +1910,35 @@
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
+call-me-maybe@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
+  integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
+
+caller-callsite@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+  dependencies:
+    callsites "^2.0.0"
+
+caller-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+  dependencies:
+    caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+  integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
 camel-case@3.0.x:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
@@ -1071,56 +1947,25 @@
     no-case "^2.2.0"
     upper-case "^1.1.1"
 
-camelcase-keys@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
-  integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
-  dependencies:
-    camelcase "^2.0.0"
-    map-obj "^1.0.0"
+camelcase@^5.0.0, camelcase@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
 
-camelcase@^1.0.2:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
-  integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=
-
-camelcase@^2.0.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
-  integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
-
-camelcase@^3.0.0:
+caniuse-api@^3.0.0:
   version "3.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
-  integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
-
-caniuse-api@^1.5.2:
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
-  integrity sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=
+  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
+  integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
   dependencies:
-    browserslist "^1.3.6"
-    caniuse-db "^1.0.30000529"
+    browserslist "^4.0.0"
+    caniuse-lite "^1.0.0"
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
-  version "1.0.30000984"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000984.tgz#3e4a53d78f33403e931ef1d2b2db07556d253dff"
-  integrity sha512-1tismk25It1v7bWgRHkHxITa7ySDXVQCwb49iKbn/HeDBTEKOgEqKkJT2Xv5rJSneDqdQRqFvYrzvw5WulLjfQ==
-
-caniuse-lite@^1.0.30000844:
-  version "1.0.30000984"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz#dc96c3c469e9bcfc6ad5bdd24c77ec918ea76fe0"
-  integrity sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==
-
-center-align@^0.1.1:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
-  integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60=
-  dependencies:
-    align-text "^0.1.3"
-    lazy-cache "^1.0.3"
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001111:
+  version "1.0.30001122"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001122.tgz#2c8ff631330d986a07a7ba7125cce77a1373b475"
+  integrity sha512-pxjw28CThdrqfz06nJkpAc5SXM404TXB/h5f4UJX+rrXJKE/1bu/KAILc2AY+O6cQIFtRjV9qOR2vaEp9LDGUA==
 
 chalk@^1.1.3:
   version "1.1.3"
@@ -1133,7 +1978,7 @@
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-chalk@^2.4.1:
+chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -1142,10 +1987,26 @@
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
-chokidar@^2.0.2, chokidar@^2.1.2:
-  version "2.1.6"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5"
-  integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==
+chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chalk@^4.0.0, chalk@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+  integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chokidar@^2.1.8:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
+  integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
   dependencies:
     anymatch "^2.0.0"
     async-each "^1.0.1"
@@ -1161,10 +2022,42 @@
   optionalDependencies:
     fsevents "^1.2.7"
 
+chokidar@^3.4.1:
+  version "3.4.2"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d"
+  integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==
+  dependencies:
+    anymatch "~3.1.1"
+    braces "~3.0.2"
+    glob-parent "~5.1.0"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.4.0"
+  optionalDependencies:
+    fsevents "~2.1.2"
+
 chownr@^1.1.1:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
-  integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
+  integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+
+chownr@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+  integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
+  integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==
+  dependencies:
+    tslib "^1.9.0"
+
+ci-info@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+  integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
 
 cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
   version "1.0.4"
@@ -1174,13 +2067,6 @@
     inherits "^2.0.1"
     safe-buffer "^5.0.1"
 
-clap@^1.0.9:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51"
-  integrity sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==
-  dependencies:
-    chalk "^1.1.3"
-
 class-utils@^0.3.5:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -1192,52 +2078,59 @@
     static-extend "^0.1.1"
 
 clean-css@4.2.x:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
-  integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
+  integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
   dependencies:
     source-map "~0.6.0"
 
-cliui@^2.1.0:
+clean-stack@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+  integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-truncate@^2.1.0:
   version "2.1.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
-  integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+  integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
   dependencies:
-    center-align "^0.1.1"
-    right-align "^0.1.1"
-    wordwrap "0.0.2"
+    slice-ansi "^3.0.0"
+    string-width "^4.2.0"
 
-cliui@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
-  integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
+cliui@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+  integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
   dependencies:
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
-    wrap-ansi "^2.0.0"
+    string-width "^3.1.0"
+    strip-ansi "^5.2.0"
+    wrap-ansi "^5.1.0"
 
-clone@^1.0.2:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
-  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
-
-co@^4.6.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
-  integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
-
-coa@~1.0.1:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd"
-  integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=
+clone-deep@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+  integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
   dependencies:
+    is-plain-object "^2.0.4"
+    kind-of "^6.0.2"
+    shallow-clone "^3.0.0"
+
+coa@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
+  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
+  dependencies:
+    "@types/q" "^1.5.1"
+    chalk "^2.4.1"
     q "^1.1.2"
 
-code-point-at@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
-  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -1246,62 +2139,60 @@
     map-visit "^1.0.0"
     object-visit "^1.0.0"
 
-color-convert@^1.3.0, color-convert@^1.9.0:
+color-convert@^1.9.0, color-convert@^1.9.1:
   version "1.9.3"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
   integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
   dependencies:
     color-name "1.1.3"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
 color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
-color-name@^1.0.0:
+color-name@^1.0.0, color-name@~1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
-color-string@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
-  integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=
+color-string@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
   dependencies:
     color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
 
-color@^0.11.0:
-  version "0.11.4"
-  resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
-  integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=
+color@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
+  integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
   dependencies:
-    clone "^1.0.2"
-    color-convert "^1.3.0"
-    color-string "^0.3.0"
-
-colormin@^1.0.5:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
-  integrity sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=
-  dependencies:
-    color "^0.11.0"
-    css-color-names "0.0.4"
-    has "^1.0.1"
-
-colors@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
-  integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
+    color-convert "^1.9.1"
+    color-string "^1.5.2"
 
 commander@2.17.x:
   version "2.17.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
   integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
 
-commander@^2.19.0:
-  version "2.20.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
-  integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+commander@^2.19.0, commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^6.0.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc"
+  integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==
 
 commander@~2.19.0:
   version "2.19.0"
@@ -1313,19 +2204,35 @@
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
   integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
 
+compare-versions@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
+  integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
+
 component-emitter@^1.2.1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
   integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
 
 compressible@~2.0.16:
-  version "2.0.17"
-  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
-  integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+  integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
   dependencies:
-    mime-db ">= 1.40.0 < 2"
+    mime-db ">= 1.43.0 < 2"
 
-compression@^1.7.3:
+compression-webpack-plugin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-4.0.1.tgz#33eda97f1170dd38c5556771de10f34245aa0274"
+  integrity sha512-0mg6PgwTsUe5LEcUrOu3ob32vraDx2VdbMGAT1PARcOV+UJWDYZFdkSo6RbHoGQ061mmmkC7XpRKOlvwm/gzJQ==
+  dependencies:
+    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"
+
+compression@^1.7.4:
   version "1.7.4"
   resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
   integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
@@ -1343,6 +2250,25 @@
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
+concat-stream@^1.5.0:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+condense-newlines@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f"
+  integrity sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-whitespace "^0.3.0"
+    kind-of "^3.0.2"
+
 config-chain@^1.1.12:
   version "1.1.12"
   resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
@@ -1351,27 +2277,20 @@
     ini "^1.3.4"
     proto-list "~1.2.1"
 
-connect-history-api-fallback@^1.3.0:
+connect-history-api-fallback@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
   integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
 
 console-browserify@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
-  integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=
-  dependencies:
-    date-now "^0.1.4"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
+  integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
 
-console-control-strings@^1.0.0, console-control-strings@~1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
-  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
-
-consolidate@^0.14.0:
-  version "0.14.5"
-  resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.5.tgz#5a25047bc76f73072667c8cb52c989888f494c63"
-  integrity sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=
+consolidate@^0.15.1:
+  version "0.15.1"
+  resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
+  integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==
   dependencies:
     bluebird "^3.1.1"
 
@@ -1392,10 +2311,10 @@
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
   integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
 
-convert-source-map@^1.5.1:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
-  integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
+convert-source-map@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+  integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
   dependencies:
     safe-buffer "~5.1.1"
 
@@ -1409,43 +2328,87 @@
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
   integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
 
+copy-concurrently@^1.0.0:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
+  integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
+  dependencies:
+    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"
+
 copy-descriptor@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
 
-core-js@^2.4.0, core-js@^2.5.0:
-  version "2.6.9"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
-  integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
+core-js-compat@^3.6.2:
+  version "3.6.5"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c"
+  integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==
+  dependencies:
+    browserslist "^4.8.5"
+    semver "7.0.0"
+
+core-js-pure@^3.0.0:
+  version "3.6.5"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
+  integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
+
+core-js@^2.6.5:
+  version "2.6.11"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
+  integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
 
 core-util-is@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
   integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
 
-cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892"
-  integrity sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==
+cosmiconfig@^5.0.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
   dependencies:
+    import-fresh "^2.0.0"
     is-directory "^0.3.1"
-    js-yaml "^3.4.3"
-    minimist "^1.2.0"
-    object-assign "^4.1.0"
-    os-homedir "^1.0.1"
-    parse-json "^2.2.0"
-    require-from-string "^1.1.0"
+    js-yaml "^3.13.1"
+    parse-json "^4.0.0"
+
+cosmiconfig@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
+  integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
+  dependencies:
+    "@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"
+
+cosmiconfig@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3"
+  integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
 
 create-ecdh@^4.0.0:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
-  integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
+  integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==
   dependencies:
     bn.js "^4.1.0"
-    elliptic "^6.0.0"
+    elliptic "^6.5.3"
 
-create-hash@^1.1.0, create-hash@^1.1.2:
+create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
   integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
@@ -1456,7 +2419,7 @@
     ripemd160 "^2.0.1"
     sha.js "^2.4.0"
 
-create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
   integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
@@ -1468,23 +2431,33 @@
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-cross-env@^3.0.0:
-  version "3.2.4"
-  resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-3.2.4.tgz#9e0585f277864ed421ce756f81a980ff0d698aba"
-  integrity sha1-ngWF8neGTtQhznVvgamA/w1piro=
+cross-env@^7.0.2:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"
+  integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==
   dependencies:
-    cross-spawn "^5.1.0"
-    is-windows "^1.0.0"
+    cross-spawn "^7.0.1"
 
-cross-spawn@^5.1.0:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
-  integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
+cross-spawn@^6.0.0, cross-spawn@^6.0.5:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
   dependencies:
-    lru-cache "^4.0.1"
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
     shebang-command "^1.2.0"
     which "^1.2.9"
 
+cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
 crypto-browserify@^3.11.0:
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -1502,28 +2475,42 @@
     randombytes "^2.0.0"
     randomfill "^1.0.3"
 
-css-color-names@0.0.4:
+css-color-names@0.0.4, css-color-names@^0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
   integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
 
-css-loader@^0.25.0:
-  version "0.25.0"
-  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.25.0.tgz#c3febc8ce28f4c83576b6b13707f47f90c390223"
-  integrity sha1-w/68jOKPTINXa2sTcH9H+Qw5AiM=
+css-declaration-sorter@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22"
+  integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==
   dependencies:
-    babel-code-frame "^6.11.0"
-    css-selector-tokenizer "^0.6.0"
-    cssnano ">=2.6.1 <4"
-    loader-utils "~0.2.2"
-    lodash.camelcase "^3.0.1"
-    object-assign "^4.0.1"
-    postcss "^5.0.6"
-    postcss-modules-extract-imports "^1.0.0"
-    postcss-modules-local-by-default "^1.0.1"
-    postcss-modules-scope "^1.0.0"
-    postcss-modules-values "^1.1.0"
-    source-list-map "^0.1.4"
+    postcss "^7.0.1"
+    timsort "^0.3.0"
+
+css-loader@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645"
+  integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==
+  dependencies:
+    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.32"
+    postcss-modules-extract-imports "^2.0.0"
+    postcss-modules-local-by-default "^3.0.2"
+    postcss-modules-scope "^2.2.0"
+    postcss-modules-values "^3.0.0"
+    postcss-value-parser "^4.1.0"
+    schema-utils "^2.7.0"
+    semver "^6.3.0"
+
+css-select-base-adapter@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
+  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
 
 css-select@^1.1.0:
   version "1.2.0"
@@ -1535,119 +2522,154 @@
     domutils "1.5.1"
     nth-check "~1.0.1"
 
-css-selector-tokenizer@^0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152"
-  integrity sha1-ZEX1gseTDSQdzFAHpD1vy48HMVI=
+css-select@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
+  integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
   dependencies:
-    cssesc "^0.1.0"
-    fastparse "^1.1.1"
-    regexpu-core "^1.0.0"
+    boolbase "^1.0.0"
+    css-what "^3.2.1"
+    domutils "^1.7.0"
+    nth-check "^1.0.2"
 
-css-selector-tokenizer@^0.7.0:
-  version "0.7.1"
-  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d"
-  integrity sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==
+css-tree@1.0.0-alpha.37:
+  version "1.0.0-alpha.37"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
+  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
   dependencies:
-    cssesc "^0.1.0"
-    fastparse "^1.1.1"
-    regexpu-core "^1.0.0"
+    mdn-data "2.0.4"
+    source-map "^0.6.1"
+
+css-tree@1.0.0-alpha.39:
+  version "1.0.0-alpha.39"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
+  integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
+  dependencies:
+    mdn-data "2.0.6"
+    source-map "^0.6.1"
 
 css-what@2.1:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
   integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
 
-cssesc@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
-  integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=
+css-what@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
+  integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
 
-"cssnano@>=2.6.1 <4":
-  version "3.10.0"
-  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
-  integrity sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano-preset-default@^4.0.7:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
+  integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==
   dependencies:
-    autoprefixer "^6.3.1"
-    decamelize "^1.1.2"
-    defined "^1.0.0"
-    has "^1.0.1"
-    object-assign "^4.0.1"
-    postcss "^5.0.14"
-    postcss-calc "^5.2.0"
-    postcss-colormin "^2.1.8"
-    postcss-convert-values "^2.3.4"
-    postcss-discard-comments "^2.0.4"
-    postcss-discard-duplicates "^2.0.1"
-    postcss-discard-empty "^2.0.1"
-    postcss-discard-overridden "^0.1.1"
-    postcss-discard-unused "^2.2.1"
-    postcss-filter-plugins "^2.0.0"
-    postcss-merge-idents "^2.1.5"
-    postcss-merge-longhand "^2.0.1"
-    postcss-merge-rules "^2.0.3"
-    postcss-minify-font-values "^1.0.2"
-    postcss-minify-gradients "^1.0.1"
-    postcss-minify-params "^1.0.4"
-    postcss-minify-selectors "^2.0.4"
-    postcss-normalize-charset "^1.1.0"
-    postcss-normalize-url "^3.0.7"
-    postcss-ordered-values "^2.1.0"
-    postcss-reduce-idents "^2.2.2"
-    postcss-reduce-initial "^1.0.0"
-    postcss-reduce-transforms "^1.0.3"
-    postcss-svgo "^2.1.1"
-    postcss-unique-selectors "^2.0.2"
-    postcss-value-parser "^3.2.3"
-    postcss-zindex "^2.0.1"
+    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"
 
-csso@~2.3.1:
-  version "2.3.2"
-  resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
-  integrity sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=
+cssnano-util-get-arguments@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f"
+  integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=
+
+cssnano-util-get-match@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d"
+  integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=
+
+cssnano-util-raw-cache@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282"
+  integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==
   dependencies:
-    clap "^1.0.9"
-    source-map "^0.5.3"
+    postcss "^7.0.0"
 
-currently-unhandled@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
-  integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
+cssnano-util-same-parent@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
+  integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
+
+cssnano@^4.1.10:
+  version "4.1.10"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
+  integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
   dependencies:
-    array-find-index "^1.0.1"
+    cosmiconfig "^5.0.0"
+    cssnano-preset-default "^4.0.7"
+    is-resolvable "^1.0.0"
+    postcss "^7.0.0"
 
-date-now@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
-  integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
+csso@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
+  integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
+  dependencies:
+    css-tree "1.0.0-alpha.39"
+
+cyclist@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
+  integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
 de-indent@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
   integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
 
-debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
   dependencies:
     ms "2.0.0"
 
-debug@^3.1.0, debug@^3.2.6:
+debug@^3.1.1, debug@^3.2.5:
   version "3.2.6"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
   integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
   dependencies:
     ms "^2.1.1"
 
-debug@^4.1.0:
+debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
   integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
   dependencies:
     ms "^2.1.1"
 
-decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
+decamelize@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -1657,17 +2679,37 @@
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
   integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
 
+dedent@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
+  integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
+
 deep-equal@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
-  integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
+  integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
+  dependencies:
+    is-arguments "^1.0.4"
+    is-date-object "^1.0.1"
+    is-regex "^1.0.4"
+    object-is "^1.0.1"
+    object-keys "^1.1.1"
+    regexp.prototype.flags "^1.2.0"
 
-deep-extend@^0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
-  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+deep-is@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
 
-define-properties@^1.1.2:
+default-gateway@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
+  integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==
+  dependencies:
+    execa "^1.0.0"
+    ip-regex "^2.1.0"
+
+define-properties@^1.1.2, define-properties@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
   integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -1696,27 +2738,18 @@
     is-descriptor "^1.0.2"
     isobject "^3.0.1"
 
-defined@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
-  integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
-
-del@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
-  integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+del@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
+  integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
   dependencies:
+    "@types/glob" "^7.1.1"
     globby "^6.1.0"
-    is-path-cwd "^1.0.0"
-    is-path-in-cwd "^1.0.0"
-    p-map "^1.1.1"
-    pify "^3.0.0"
-    rimraf "^2.2.8"
-
-delegates@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
-  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+    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"
 
 depd@~1.1.2:
   version "1.1.2"
@@ -1724,9 +2757,9 @@
   integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
 
 des.js@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
-  integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
+  integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
   dependencies:
     inherits "^2.0.1"
     minimalistic-assert "^1.0.0"
@@ -1736,17 +2769,10 @@
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
   integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
 
-detect-indent@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
-  integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg=
-  dependencies:
-    repeating "^2.0.0"
-
-detect-libc@^1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
-  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+detect-file@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
+  integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
 
 detect-node@^2.0.4:
   version "2.0.4"
@@ -1762,6 +2788,13 @@
     miller-rabin "^4.0.0"
     randombytes "^2.0.0"
 
+dir-glob@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+  integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+  dependencies:
+    path-type "^4.0.0"
+
 dns-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -1782,6 +2815,18 @@
   dependencies:
     buffer-indexof "^1.0.0"
 
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+dom-accessibility-api@^0.5.1:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.3.tgz#0ea493c924d4070dfbf531c4aaca3d7a2c601aab"
+  integrity sha512-yfqzAi1GFxK6EoJIZKgxqJyK6j/OjEFEUi2qkNThD/kUhoCFSG1izq31B5xuxzbJBGw9/67uPtkPMYAzWL7L7Q==
+
 dom-converter@^0.2:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@@ -1789,24 +2834,34 @@
   dependencies:
     utila "~0.4"
 
+dom-event-types@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.0.0.tgz#5830a0a29e1bf837fe50a70cd80a597232813cae"
+  integrity sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==
+
 dom-serializer@0:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
-  integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
   dependencies:
-    domelementtype "^1.3.0"
-    entities "^1.1.1"
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
 
 domain-browser@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
   integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
 
-domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+domelementtype@1, domelementtype@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
   integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
 
+domelementtype@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+  integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
 domhandler@^2.3.0:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
@@ -1822,7 +2877,7 @@
     dom-serializer "0"
     domelementtype "1"
 
-domutils@^1.5.1:
+domutils@^1.5.1, domutils@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
   integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
@@ -1830,6 +2885,23 @@
     dom-serializer "0"
     domelementtype "1"
 
+dot-prop@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
+  integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
+  dependencies:
+    is-obj "^2.0.0"
+
+duplexify@^3.4.2, duplexify@^3.6.0:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
+  integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
+  dependencies:
+    end-of-stream "^1.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+    stream-shift "^1.0.0"
+
 editorconfig@^0.15.3:
   version "0.15.3"
   resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
@@ -1845,15 +2917,15 @@
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
-electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.47:
-  version "1.3.194"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.194.tgz#a96452a96d4539131957aade9f634a45721f2819"
-  integrity sha512-w0LHR2YD9Ex1o+Sz4IN2hYzCB8vaFtMNW+yJcBf6SZlVqgFahkne/4rGVJdk4fPF98Gch9snY7PiabOh+vqHNg==
+electron-to-chromium@^1.3.523:
+  version "1.3.558"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.558.tgz#a329d3139cb33e8652a7e7db4c5ae26e294b9f60"
+  integrity sha512-r6th6b/TU2udqVoUDGWHF/z2ACJVnEei0wvWZf/nt+Qql1Vxh60ZYPhQP46j4D73T/Jou7hl4TqQfxben+qJTg==
 
-elliptic@^6.0.0:
-  version "6.5.0"
-  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca"
-  integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==
+elliptic@^6.5.3:
+  version "6.5.3"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
+  integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
   dependencies:
     bn.js "^4.4.0"
     brorand "^1.0.1"
@@ -1863,66 +2935,125 @@
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.0"
 
+emoji-regex@^7.0.1:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
   integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
 
+emojis-list@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
 encodeurl@~1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
   integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 
-enhanced-resolve@^3.3.0:
-  version "3.4.1"
-  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
-  integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=
+end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+  dependencies:
+    once "^1.4.0"
+
+enhanced-resolve@^4.0.0, enhanced-resolve@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126"
+  integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==
   dependencies:
     graceful-fs "^4.1.2"
-    memory-fs "^0.4.0"
-    object-assign "^4.0.1"
-    tapable "^0.2.7"
+    memory-fs "^0.5.0"
+    tapable "^1.0.0"
+
+enhanced-resolve@^4.1.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d"
+  integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.5.0"
+    tapable "^1.0.0"
+
+enquirer@^2.3.5, enquirer@^2.3.6:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+  integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
+  dependencies:
+    ansi-colors "^4.1.1"
 
 entities@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
   integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
 
-errno@^0.1.3:
+entities@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
+  integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+
+errno@^0.1.3, errno@~0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
   integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
   dependencies:
     prr "~1.0.1"
 
-error-ex@^1.2.0:
+error-ex@^1.3.1:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
   integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.7.0:
-  version "1.13.0"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
-  integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==
+error-stack-parser@^2.0.0:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8"
+  integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==
   dependencies:
-    es-to-primitive "^1.2.0"
+    stackframe "^1.1.1"
+
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
+  version "1.17.6"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
+  integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
+  dependencies:
+    es-to-primitive "^1.2.1"
     function-bind "^1.1.1"
     has "^1.0.3"
-    is-callable "^1.1.4"
-    is-regex "^1.0.4"
-    object-keys "^1.0.12"
+    has-symbols "^1.0.1"
+    is-callable "^1.2.0"
+    is-regex "^1.1.0"
+    object-inspect "^1.7.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.0"
+    string.prototype.trimend "^1.0.1"
+    string.prototype.trimstart "^1.0.1"
 
-es-to-primitive@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
-  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
+es-to-primitive@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
   dependencies:
     is-callable "^1.1.4"
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
+escalade@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
+  integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==
+
 escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -1933,42 +3064,164 @@
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
 
-esprima@^2.6.0:
-  version "2.7.3"
-  resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
-  integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
+eslint-config-google@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a"
+  integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==
+
+eslint-plugin-vue@^6.2.2:
+  version "6.2.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz#27fecd9a3a24789b0f111ecdd540a9e56198e0fe"
+  integrity sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==
+  dependencies:
+    natural-compare "^1.4.0"
+    semver "^5.6.0"
+    vue-eslint-parser "^7.0.0"
+
+eslint-scope@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
+  integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^5.0.0, eslint-scope@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
+  integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+  integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+  dependencies:
+    eslint-visitor-keys "^1.1.0"
+
+eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint@^7.1.0:
+  version "7.8.1"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.8.1.tgz#e59de3573fb6a5be8ff526c791571646d124a8fa"
+  integrity sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@eslint/eslintrc" "^0.1.3"
+    ajv "^6.10.0"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.0.1"
+    doctrine "^3.0.0"
+    enquirer "^2.3.5"
+    eslint-scope "^5.1.0"
+    eslint-utils "^2.1.0"
+    eslint-visitor-keys "^1.3.0"
+    espree "^7.3.0"
+    esquery "^1.2.0"
+    esutils "^2.0.2"
+    file-entry-cache "^5.0.1"
+    functional-red-black-tree "^1.0.1"
+    glob-parent "^5.0.0"
+    globals "^12.1.0"
+    ignore "^4.0.6"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash "^4.17.19"
+    minimatch "^3.0.4"
+    natural-compare "^1.4.0"
+    optionator "^0.9.1"
+    progress "^2.0.0"
+    regexpp "^3.1.0"
+    semver "^7.2.1"
+    strip-ansi "^6.0.0"
+    strip-json-comments "^3.1.0"
+    table "^5.2.3"
+    text-table "^0.2.0"
+    v8-compile-cache "^2.0.3"
+
+espree@^6.2.1:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
+  integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==
+  dependencies:
+    acorn "^7.1.1"
+    acorn-jsx "^5.2.0"
+    eslint-visitor-keys "^1.1.0"
+
+espree@^7.3.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348"
+  integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==
+  dependencies:
+    acorn "^7.4.0"
+    acorn-jsx "^5.2.0"
+    eslint-visitor-keys "^1.3.0"
 
 esprima@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
   integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
 
+esquery@^1.0.1, esquery@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57"
+  integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
+  integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
+
 esutils@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
-  integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
 
 etag@~1.8.1:
   version "1.8.1"
   resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
   integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
 
-eventemitter3@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
-  integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
+eventemitter3@^4.0.0:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+  integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
 
 events@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
-  integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379"
+  integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==
 
-eventsource@0.1.6:
-  version "0.1.6"
-  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
-  integrity sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=
+eventsource@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
+  integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==
   dependencies:
-    original ">=0.0.5"
+    original "^1.0.0"
 
 evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
   version "1.0.3"
@@ -1978,6 +3231,34 @@
     md5.js "^1.3.4"
     safe-buffer "^5.1.1"
 
+execa@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+  dependencies:
+    cross-spawn "^6.0.0"
+    get-stream "^4.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+execa@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2"
+  integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==
+  dependencies:
+    cross-spawn "^7.0.0"
+    get-stream "^5.0.0"
+    human-signals "^1.1.1"
+    is-stream "^2.0.0"
+    merge-stream "^2.0.0"
+    npm-run-path "^4.0.0"
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+    strip-final-newline "^2.0.0"
+
 expand-brackets@^2.1.4:
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -1991,7 +3272,14 @@
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-express@^4.16.2:
+expand-tilde@^2.0.0, expand-tilde@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
+  integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
+  dependencies:
+    homedir-polyfill "^1.0.1"
+
+express@^4.17.1:
   version "4.17.1"
   resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
   integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
@@ -2056,20 +3344,51 @@
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-fast-deep-equal@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
-  integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
+fast-deep-equal@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@^2.2.6:
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
+  integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
+  dependencies:
+    "@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"
+
+fast-glob@^3.0.3:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
+  integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.0"
+    merge2 "^1.3.0"
+    micromatch "^4.0.2"
+    picomatch "^2.2.1"
 
 fast-json-stable-stringify@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
-  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
 
-fastparse@^1.1.1:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
-  integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fastq@^1.6.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
+  integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
+  dependencies:
+    reusify "^1.0.4"
 
 faye-websocket@^0.10.0:
   version "0.10.0"
@@ -2078,19 +3397,44 @@
   dependencies:
     websocket-driver ">=0.5.1"
 
-faye-websocket@~0.11.0:
+faye-websocket@~0.11.1:
   version "0.11.3"
   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
   integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==
   dependencies:
     websocket-driver ">=0.5.1"
 
-file-loader@^0.9.0:
-  version "0.9.0"
-  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.9.0.tgz#1d2daddd424ce6d1b07cfe3f79731bed3617ab42"
-  integrity sha1-HS2t3UJM5tGwfP4/eXMb7TYXq0I=
+figgy-pudding@^3.5.1:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
+  integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
+
+figures@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+  integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
   dependencies:
-    loader-utils "~0.2.5"
+    escape-string-regexp "^1.0.5"
+
+file-entry-cache@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
+  integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
+  dependencies:
+    flat-cache "^2.0.1"
+
+file-loader@^6.0.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.0.tgz#65b9fcfb0ea7f65a234a1f10cdd7f1ab9a33f253"
+  integrity sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg==
+  dependencies:
+    loader-utils "^2.0.0"
+    schema-utils "^2.7.1"
+
+file-uri-to-path@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+  integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
 
 fill-range@^4.0.0:
   version "4.0.0"
@@ -2102,6 +3446,13 @@
     repeat-string "^1.6.1"
     to-regex-range "^2.1.0"
 
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
 finalhandler@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -2115,47 +3466,93 @@
     statuses "~1.5.0"
     unpipe "~1.0.0"
 
-find-cache-dir@^0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
-  integrity sha1-yN765XyKUqinhPnjHFfHQumToLk=
+find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+  integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
   dependencies:
     commondir "^1.0.1"
-    mkdirp "^0.5.1"
-    pkg-dir "^1.0.0"
+    make-dir "^2.0.0"
+    pkg-dir "^3.0.0"
 
-find-up@^1.0.0:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
-  integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
+find-cache-dir@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880"
+  integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==
   dependencies:
-    path-exists "^2.0.0"
-    pinkie-promise "^2.0.0"
+    commondir "^1.0.1"
+    make-dir "^3.0.2"
+    pkg-dir "^4.1.0"
 
-find-up@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
-  integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
   dependencies:
-    locate-path "^2.0.0"
+    locate-path "^3.0.0"
 
-flatten@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
-  integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
+find-up@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
+find-versions@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e"
+  integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==
+  dependencies:
+    semver-regex "^2.0.0"
+
+findup-sync@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1"
+  integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==
+  dependencies:
+    detect-file "^1.0.0"
+    is-glob "^4.0.0"
+    micromatch "^3.0.4"
+    resolve-dir "^1.0.1"
+
+flat-cache@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
+  integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
+  dependencies:
+    flatted "^2.0.0"
+    rimraf "2.6.3"
+    write "1.0.3"
+
+flatted@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
+  integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
+
+flush-write-stream@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
+  integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.3.6"
 
 follow-redirects@^1.0.0:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
-  integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==
-  dependencies:
-    debug "^3.2.6"
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
+  integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
 
 for-in@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
   integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
 
+format-thousands@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/format-thousands/-/format-thousands-1.1.1.tgz#7975bee30338d9006390da5831db0b41c323fbfa"
+  integrity sha1-eXW+4wM42QBjkNpYMdsLQcMj+/o=
+
 forwarded@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -2173,12 +3570,48 @@
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
   integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
 
-fs-minipass@^1.2.5:
-  version "1.2.6"
-  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
-  integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==
+friendly-errors-webpack-plugin@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0.tgz#efc86cbb816224565861a1be7a9d84d0aafea136"
+  integrity sha512-K27M3VK30wVoOarP651zDmb93R9zF28usW4ocaK3mfQeIEI5BPht/EzZs5E8QLLwbLRJQMwscAjDxYPb1FuNiw==
   dependencies:
-    minipass "^2.2.1"
+    chalk "^1.1.3"
+    error-stack-parser "^2.0.0"
+    string-width "^2.0.0"
+
+from2@^2.1.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
+  integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
+  dependencies:
+    inherits "^2.0.1"
+    readable-stream "^2.0.0"
+
+fs-extra@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs-minipass@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+  integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+  dependencies:
+    minipass "^3.0.0"
+
+fs-write-stream-atomic@^1.0.8:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
+  integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
+  dependencies:
+    graceful-fs "^4.1.2"
+    iferr "^0.1.5"
+    imurmurhash "^0.1.4"
+    readable-stream "1 || 2"
 
 fs.realpath@^1.0.0:
   version "1.0.0"
@@ -2186,47 +3619,67 @@
   integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
 
 fsevents@^1.2.7:
-  version "1.2.9"
-  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
-  integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38"
+  integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==
   dependencies:
+    bindings "^1.5.0"
     nan "^2.12.1"
-    node-pre-gyp "^0.12.0"
+
+fsevents@~2.1.2:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
+  integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
 
 function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
-gauge@~2.7.3:
-  version "2.7.4"
-  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
-  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+functional-red-black-tree@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+gensync@^1.0.0-beta.1:
+  version "1.0.0-beta.1"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
+  integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+
+get-caller-file@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-own-enumerable-property-symbols@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
+  integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
+
+get-stream@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
   dependencies:
-    aproba "^1.0.3"
-    console-control-strings "^1.0.0"
-    has-unicode "^2.0.0"
-    object-assign "^4.1.0"
-    signal-exit "^3.0.0"
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
-    wide-align "^1.1.0"
+    pump "^3.0.0"
 
-get-caller-file@^1.0.1:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
-  integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
-
-get-stdin@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
-  integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
+get-stream@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+  integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
+  dependencies:
+    pump "^3.0.0"
 
 get-value@^2.0.3, get-value@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
   integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
 
+github-buttons@^2.8.0:
+  version "2.14.0"
+  resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.14.0.tgz#32ce381651091accda09217cd7a6e4c77f91e222"
+  integrity sha512-rAwKwFOiWoyhb3g5ZyXjI3XXprAa36jCd0tm467aEUYtiDZkqEXkepuzNg9LryLbnuLRQmcifIPTxLUBnuYpXQ==
+
 glob-parent@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -2235,10 +3688,22 @@
     is-glob "^3.1.0"
     path-dirname "^1.0.0"
 
-glob@^7.0.3, glob@^7.1.3:
-  version "7.1.4"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
-  integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
+glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
+  integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-to-regexp@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
+  integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+
+glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -2247,10 +3712,67 @@
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-globals@^9.18.0:
-  version "9.18.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
-  integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
+global-modules@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
+  integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
+  dependencies:
+    global-prefix "^1.0.1"
+    is-windows "^1.0.1"
+    resolve-dir "^1.0.0"
+
+global-modules@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
+  integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
+  dependencies:
+    global-prefix "^3.0.0"
+
+global-prefix@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
+  integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=
+  dependencies:
+    expand-tilde "^2.0.2"
+    homedir-polyfill "^1.0.1"
+    ini "^1.3.4"
+    is-windows "^1.0.1"
+    which "^1.2.14"
+
+global-prefix@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+  integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+  dependencies:
+    ini "^1.3.5"
+    kind-of "^6.0.2"
+    which "^1.3.1"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globals@^12.1.0:
+  version "12.4.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
+  integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==
+  dependencies:
+    type-fest "^0.8.1"
+
+globby@^10.0.1:
+  version "10.0.2"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
+  integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==
+  dependencies:
+    "@types/glob" "^7.1.1"
+    array-union "^2.1.0"
+    dir-glob "^3.0.1"
+    fast-glob "^3.0.3"
+    glob "^7.1.3"
+    ignore "^5.1.1"
+    merge2 "^1.2.3"
+    slash "^3.0.0"
 
 globby@^6.1.0:
   version "6.1.0"
@@ -2263,15 +3785,15 @@
     pify "^2.0.0"
     pinkie-promise "^2.0.0"
 
-graceful-fs@^4.1.11, graceful-fs@^4.1.2:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b"
-  integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.4"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
+  integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
 
 handle-thing@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
-  integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
+  integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==
 
 has-ansi@^2.0.0:
   version "2.0.0"
@@ -2280,25 +3802,20 @@
   dependencies:
     ansi-regex "^2.0.0"
 
-has-flag@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
-  integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
-
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
-has-symbols@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
-  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
-has-unicode@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
-  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+has-symbols@^1.0.0, has-symbols@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+  integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
 
 has-value@^0.3.1:
   version "0.3.1"
@@ -2331,7 +3848,7 @@
     is-number "^3.0.0"
     kind-of "^4.0.0"
 
-has@^1.0.1, has@^1.0.3:
+has@^1.0.0, has@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
   integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
@@ -2339,12 +3856,13 @@
     function-bind "^1.1.1"
 
 hash-base@^3.0.0:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
-  integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
+  integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==
   dependencies:
-    inherits "^2.0.1"
-    safe-buffer "^5.0.1"
+    inherits "^2.0.4"
+    readable-stream "^3.6.0"
+    safe-buffer "^5.2.0"
 
 hash-sum@^1.0.2:
   version "1.0.2"
@@ -2364,6 +3882,11 @@
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
+hex-color-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
+  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
+
 hmac-drbg@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -2373,18 +3896,12 @@
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.1"
 
-home-or-tmp@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
-  integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg=
+homedir-polyfill@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
+  integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
   dependencies:
-    os-homedir "^1.0.0"
-    os-tmpdir "^1.0.1"
-
-hosted-git-info@^2.1.4:
-  version "2.7.1"
-  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
-  integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
+    parse-passwd "^1.0.0"
 
 hpack.js@^2.1.6:
   version "2.1.6"
@@ -2396,15 +3913,25 @@
     readable-stream "^2.0.1"
     wbuf "^1.1.0"
 
+hsl-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e"
+  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
+
+hsla-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
+  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
+
 html-comment-regex@^1.1.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
   integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
 
-html-entities@^1.2.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
-  integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
+html-entities@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
+  integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
 
 html-minifier@^3.2.3:
   version "3.5.21"
@@ -2419,26 +3946,27 @@
     relateurl "0.2.x"
     uglify-js "3.4.x"
 
-html-webpack-inline-source-plugin@^0.0.9:
-  version "0.0.9"
-  resolved "https://registry.yarnpkg.com/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-0.0.9.tgz#e52dd3cf1164ac76ceaf39ba206f92e3597d054d"
-  integrity sha1-5S3TzxFkrHbOrzm6IG+S41l9BU0=
+html-webpack-inline-source-plugin@^0.0.10:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/html-webpack-inline-source-plugin/-/html-webpack-inline-source-plugin-0.0.10.tgz#89bd5f761e4f16902aa76a44476eb52831c9f7f0"
+  integrity sha512-0ZNU57u7283vrXSF5a4VDnVOMWiSwypKIp1z/XfXWoVHLA1r3Xmyxx5+Lz+mnthz/UvxL1OAf41w5UIF68Jngw==
   dependencies:
     escape-string-regexp "^1.0.5"
     slash "^1.0.0"
     source-map-url "^0.4.0"
 
-html-webpack-plugin@^2.30.1:
-  version "2.30.1"
-  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5"
-  integrity sha1-f5xCG36pHsRg9WUn1430hO51N9U=
+html-webpack-plugin@3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
+  integrity sha1-sBq71yOsqqeze2r0SS69oD2d03s=
   dependencies:
-    bluebird "^3.4.7"
     html-minifier "^3.2.3"
     loader-utils "^0.2.16"
     lodash "^4.17.3"
     pretty-error "^2.0.2"
+    tapable "^1.0.0"
     toposort "^1.0.0"
+    util.promisify "1.0.0"
 
 htmlparser2@^3.3.0:
   version "3.10.1"
@@ -2489,12 +4017,12 @@
     statuses ">= 1.5.0 < 2"
     toidentifier "1.0.0"
 
-"http-parser-js@>=0.4.0 <0.4.11":
-  version "0.4.10"
-  resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4"
-  integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=
+http-parser-js@>=0.5.1:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77"
+  integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==
 
-http-proxy-middleware@^0.19.1:
+http-proxy-middleware@0.19.1:
   version "0.19.1"
   resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
   integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
@@ -2505,11 +4033,11 @@
     micromatch "^3.1.10"
 
 http-proxy@^1.17.0:
-  version "1.17.0"
-  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a"
-  integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==
+  version "1.18.1"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+  integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
   dependencies:
-    eventemitter3 "^3.0.0"
+    eventemitter3 "^4.0.0"
     follow-redirects "^1.0.0"
     requires-port "^1.0.0"
 
@@ -2518,50 +4046,117 @@
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
   integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
 
-iconv-lite@0.4.24, iconv-lite@^0.4.4:
+human-signals@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
+  integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
+
+husky@^4.2.5:
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.5.tgz#2b4f7622673a71579f901d9885ed448394b5fa36"
+  integrity sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==
+  dependencies:
+    chalk "^4.0.0"
+    ci-info "^2.0.0"
+    compare-versions "^3.6.0"
+    cosmiconfig "^6.0.0"
+    find-versions "^3.2.0"
+    opencollective-postinstall "^2.0.2"
+    pkg-dir "^4.2.0"
+    please-upgrade-node "^3.2.0"
+    slash "^3.0.0"
+    which-pm-runs "^1.0.0"
+
+iconv-lite@0.4.24:
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
-icss-replace-symbols@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
-  integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+iconv-lite@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
+  integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3.0.0"
+
+icss-utils@^4.0.0, icss-utils@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
+  integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==
+  dependencies:
+    postcss "^7.0.14"
 
 ieee754@^1.1.4:
   version "1.1.13"
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
   integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
 
-ignore-walk@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
-  integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
-  dependencies:
-    minimatch "^3.0.4"
+iferr@^0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
+  integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
 
-import-local@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
-  integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==
+ignore@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
+ignore@^5.1.1:
+  version "5.1.8"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
+  integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+
+immediate@~3.0.5:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+  integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
+
+import-fresh@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
   dependencies:
-    pkg-dir "^2.0.0"
+    caller-path "^2.0.0"
+    resolve-from "^3.0.0"
+
+import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
+  integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+import-local@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+  integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+  dependencies:
+    pkg-dir "^3.0.0"
     resolve-cwd "^2.0.0"
 
-indent-string@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
-  integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=
-  dependencies:
-    repeating "^2.0.0"
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+indent-string@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+  integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
 indexes-of@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
   integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
 
+infer-owner@^1.0.3, infer-owner@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
+  integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
+
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -2570,7 +4165,7 @@
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -2585,50 +4180,56 @@
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
   integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
 
-ini@^1.3.4, ini@~1.3.0:
+ini@^1.3.4, ini@^1.3.5:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
   integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
 
-internal-ip@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
-  integrity sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=
+internal-ip@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
+  integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==
   dependencies:
-    meow "^3.3.0"
+    default-gateway "^4.2.0"
+    ipaddr.js "^1.9.0"
 
-interpret@^1.0.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
-  integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
+interpret@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
+  integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
 
-invariant@^2.2.2:
+invariant@^2.2.2, invariant@^2.2.4:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
   integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
   dependencies:
     loose-envify "^1.0.0"
 
-invert-kv@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
-  integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
+ip-regex@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
+  integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
 
 ip@^1.1.0, ip@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
   integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
 
-ipaddr.js@1.9.0:
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
-  integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
+ipaddr.js@1.9.1, ipaddr.js@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
 
 is-absolute-url@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
   integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
 
+is-absolute-url@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
+  integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
+
 is-accessor-descriptor@^0.1.6:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@@ -2643,11 +4244,21 @@
   dependencies:
     kind-of "^6.0.0"
 
+is-arguments@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
+  integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
   integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
 
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
 is-binary-path@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
@@ -2655,15 +4266,34 @@
   dependencies:
     binary-extensions "^1.0.0"
 
+is-binary-path@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+  dependencies:
+    binary-extensions "^2.0.0"
+
 is-buffer@^1.1.5:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
-is-callable@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
-  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
+is-callable@^1.1.4, is-callable@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
+  integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
+
+is-color-stop@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
+  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
+  dependencies:
+    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-data-descriptor@^0.1.4:
   version "0.1.4"
@@ -2680,9 +4310,9 @@
     kind-of "^6.0.0"
 
 is-date-object@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
-  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
+  integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
 
 is-descriptor@^0.1.0:
   version "0.1.6"
@@ -2724,25 +4354,16 @@
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
 
-is-finite@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
-  integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=
-  dependencies:
-    number-is-nan "^1.0.0"
-
-is-fullwidth-code-point@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
-  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
-  dependencies:
-    number-is-nan "^1.0.0"
-
 is-fullwidth-code-point@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
   integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
 
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
 is-glob@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
@@ -2750,7 +4371,7 @@
   dependencies:
     is-extglob "^2.1.0"
 
-is-glob@^4.0.0:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@@ -2764,24 +4385,39 @@
   dependencies:
     kind-of "^3.0.2"
 
-is-path-cwd@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
-  integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
 
-is-path-in-cwd@^1.0.0:
+is-obj@^1.0.1:
   version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
-  integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
-  dependencies:
-    is-path-inside "^1.0.0"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+  integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
 
-is-path-inside@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
-  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+is-obj@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-path-cwd@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
+  integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
+
+is-path-in-cwd@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
+  integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
   dependencies:
-    path-is-inside "^1.0.1"
+    is-path-inside "^2.1.0"
+
+is-path-inside@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
+  integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
+  dependencies:
+    path-is-inside "^1.0.2"
 
 is-plain-obj@^1.0.0:
   version "1.1.0"
@@ -2795,33 +4431,53 @@
   dependencies:
     isobject "^3.0.1"
 
-is-regex@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
-  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
+is-regex@^1.0.4, is-regex@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
+  integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
   dependencies:
-    has "^1.0.1"
+    has-symbols "^1.0.1"
 
-is-svg@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
-  integrity sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=
+is-regexp@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+  integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
+
+is-resolvable@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
+
+is-stream@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
+  integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
+
+is-svg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
+  integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==
   dependencies:
     html-comment-regex "^1.1.0"
 
 is-symbol@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
-  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+  integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
   dependencies:
-    has-symbols "^1.0.0"
+    has-symbols "^1.0.1"
 
-is-utf8@^0.2.0:
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
-  integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
+is-whitespace@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/is-whitespace/-/is-whitespace-0.3.0.tgz#1639ecb1be036aec69a54cbb401cfbed7114ab7f"
+  integrity sha1-Fjnssb4DauxppUy7QBz77XEUq38=
 
-is-windows@^1.0.0, is-windows@^1.0.2:
+is-windows@^1.0.1, is-windows@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
   integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
@@ -2853,81 +4509,79 @@
   resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
   integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
 
-js-base64@^2.1.9:
-  version "2.5.1"
-  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
-  integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
+jasmine-core@~3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20"
+  integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==
 
-js-beautify@^1.6.3:
-  version "1.10.1"
-  resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.1.tgz#bdfe738ddbcaa12e4fced5af2d7cfad59f60ac0a"
-  integrity sha512-4y8SHOIRC+/YQ2gs3zJEKBUraQerq49FJYyXRpdzUGYQzCq8q9xtIh0YXial1S5KmonVui4aiUb6XaGyjE51XA==
+jasmine@^3.5.0:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.6.1.tgz#a20456b309a669b547a3c24bb2120f16f70cfc65"
+  integrity sha512-Jqp8P6ZWkTVFGmJwBK46p+kJNrZCdqkQ4GL+PGuBXZwK1fM4ST9BizkYgIwCFqYYqnTizAy6+XG2Ej5dFrej9Q==
+  dependencies:
+    fast-glob "^2.2.6"
+    jasmine-core "~3.6.0"
+
+js-beautify@^1.6.12:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.13.0.tgz#a056d5d3acfd4918549aae3ab039f9f3c51eebb2"
+  integrity sha512-/Tbp1OVzZjbwzwJQFIlYLm9eWQ+3aYbBXLSaqb1mEJzhcQAfrqMMQYtjb6io+U6KpD0ID4F+Id3/xcjH3l/sqA==
   dependencies:
     config-chain "^1.1.12"
     editorconfig "^0.15.3"
     glob "^7.1.3"
-    mkdirp "~0.5.1"
-    nopt "~4.0.1"
+    mkdirp "^1.0.4"
+    nopt "^5.0.0"
 
-"js-tokens@^3.0.0 || ^4.0.0":
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
   integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
 
-js-tokens@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
-  integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-
-js-yaml@^3.4.3:
-  version "3.13.1"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
-  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+js-yaml@^3.13.1:
+  version "3.14.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
+  integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
   dependencies:
     argparse "^1.0.7"
     esprima "^4.0.0"
 
-js-yaml@~3.7.0:
-  version "3.7.0"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
-  integrity sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=
-  dependencies:
-    argparse "^1.0.7"
-    esprima "^2.6.0"
-
-jsesc@^1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
-  integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
 
 jsesc@~0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
   integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
 
-json-loader@^0.5.4:
-  version "0.5.7"
-  resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
-  integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+  integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
 
-json-schema-traverse@^0.3.0:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
-  integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.0.tgz#371873c5ffa44304a6ba12419bcfa95f404ae081"
+  integrity sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q==
 
-json-stable-stringify@^1.0.1:
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
   version "1.0.1"
-  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
-  integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
-  dependencies:
-    jsonify "~0.0.0"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
 
 json3@^3.3.2:
   version "3.3.3"
   resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
   integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
 
-json5@^0.5.0, json5@^0.5.1:
+json5@^0.5.0:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
   integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
@@ -2939,12 +4593,31 @@
   dependencies:
     minimist "^1.2.0"
 
-jsonify@~0.0.0:
-  version "0.0.0"
-  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
-  integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
+json5@^2.1.2:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
+  integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
+  dependencies:
+    minimist "^1.2.5"
 
-killable@^1.0.0:
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jszip@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
+  integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
+  dependencies:
+    lie "~3.3.0"
+    pako "~1.0.2"
+    readable-stream "~2.3.6"
+    set-immediate-shim "~1.0.1"
+
+killable@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
   integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==
@@ -2969,39 +4642,101 @@
   integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
 
 kind-of@^6.0.0, kind-of@^6.0.2:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
-  integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+  integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
 
-lazy-cache@^1.0.3:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
-  integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4=
+kotlin-compiler@^1.3.61:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/kotlin-compiler/-/kotlin-compiler-1.4.0.tgz#4baf305a3ceedb4460da92dd09baa4eef353773f"
+  integrity sha512-eFKmqNcWY4Sp+O1WGL5ctxTPIP/HkqqbZUjBHpaVLlzle1OGAa3oqgdDhi/UTy4a+9vlWnv5FUw6nJ7u7JRhZA==
 
-lcid@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
-  integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
+kotlin@^1.3.72:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/kotlin/-/kotlin-1.4.0.tgz#87f4395d5de65a11f8eefd762c02b1d3ab5d6d69"
+  integrity sha512-q+Ts9Xr72eT3QkmLjHDObPAHbsKbZ/Vn0ozqrNNr7UbtpLxa26+Hn8ILORPLz1UIUTespZyfXztXJ7AO5Xl/Gg==
+
+last-call-webpack-plugin@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
+  integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==
   dependencies:
-    invert-kv "^1.0.0"
+    lodash "^4.17.5"
+    webpack-sources "^1.1.0"
 
-load-json-file@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
-  integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
+leven@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+  integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levenary@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77"
+  integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==
   dependencies:
-    graceful-fs "^4.1.2"
-    parse-json "^2.2.0"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
-    strip-bom "^2.0.0"
+    leven "^3.1.0"
 
-loader-runner@^2.3.0:
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+lie@~3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+  integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
+  dependencies:
+    immediate "~3.0.5"
+
+lines-and-columns@^1.1.6:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
+  integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
+
+lint-staged@>=10:
+  version "10.2.13"
+  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.13.tgz#b9c504683470edfc464b7d3fe3845a5a1efcd814"
+  integrity sha512-conwlukNV6aL9SiMWjFtDp5exeDnTMekdNPDZsKGnpfQuHcO0E3L3Bbf58lcR+M7vk6LpCilxDAVks/DDVBYlA==
+  dependencies:
+    chalk "^4.1.0"
+    cli-truncate "^2.1.0"
+    commander "^6.0.0"
+    cosmiconfig "^7.0.0"
+    debug "^4.1.1"
+    dedent "^0.7.0"
+    enquirer "^2.3.6"
+    execa "^4.0.3"
+    listr2 "^2.6.0"
+    log-symbols "^4.0.0"
+    micromatch "^4.0.2"
+    normalize-path "^3.0.0"
+    please-upgrade-node "^3.2.0"
+    string-argv "0.3.1"
+    stringify-object "^3.3.0"
+
+listr2@^2.6.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.6.2.tgz#4912eb01e1e2dd72ec37f3895a56bf2622d6f36a"
+  integrity sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==
+  dependencies:
+    chalk "^4.1.0"
+    cli-truncate "^2.1.0"
+    figures "^3.2.0"
+    indent-string "^4.0.0"
+    log-update "^4.0.0"
+    p-map "^4.0.0"
+    rxjs "^6.6.2"
+    through "^2.3.8"
+
+loader-runner@^2.4.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
   integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
 
-loader-utils@^0.2.16, loader-utils@~0.2.2, loader-utils@~0.2.5:
+loader-utils@^0.2.16:
   version "0.2.17"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
   integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
@@ -3011,49 +4746,43 @@
     json5 "^0.5.0"
     object-assign "^4.0.1"
 
-loader-utils@^1.0.2, loader-utils@^1.1.0:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
-  integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
+loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
+  integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
   dependencies:
     big.js "^5.2.2"
-    emojis-list "^2.0.0"
+    emojis-list "^3.0.0"
     json5 "^1.0.1"
 
-locate-path@^2.0.0:
+loader-utils@^2.0.0:
   version "2.0.0"
-  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
-  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
+  integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
   dependencies:
-    p-locate "^2.0.0"
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
     path-exists "^3.0.0"
 
-lodash._createcompounder@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz#5dd2cb55372d6e70e0e2392fb2304d6631091075"
-  integrity sha1-XdLLVTctbnDg4jkvsjBNZjEJEHU=
+locate-path@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+  integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
   dependencies:
-    lodash.deburr "^3.0.0"
-    lodash.words "^3.0.0"
+    p-locate "^4.1.0"
 
-lodash._root@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
-  integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=
-
-lodash.camelcase@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz#932c8b87f8a4377897c67197533282f97aeac298"
-  integrity sha1-kyyLh/ikN3iXxnGXUzKC+Xrqwpg=
-  dependencies:
-    lodash._createcompounder "^3.0.0"
-
-lodash.deburr@^3.0.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5"
-  integrity sha1-baj1QzSjZqfPTEx2742Aqhs2XtU=
-  dependencies:
-    lodash._root "^3.0.0"
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+  integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
 
 lodash.memoize@^4.1.2:
   version "4.1.2"
@@ -3065,54 +4794,56 @@
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash.words@^3.0.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz#4e2a8649bc08745b17c695b1a3ce8fee596623b3"
-  integrity sha1-TiqGSbwIdFsXxpWxo86P7llmI7M=
+lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.5:
+  version "4.17.20"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
+  integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+
+log-symbols@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
+  integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
   dependencies:
-    lodash._root "^3.0.0"
+    chalk "^4.0.0"
 
-lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.3, lodash@^4.17.4:
-  version "4.17.14"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
-  integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
+log-update@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+  integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+  dependencies:
+    ansi-escapes "^4.3.0"
+    cli-cursor "^3.1.0"
+    slice-ansi "^4.0.0"
+    wrap-ansi "^6.2.0"
 
-loglevel@^1.4.1:
-  version "1.6.3"
-  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280"
-  integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==
+loglevel@^1.6.8:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
+  integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
+
+loglevelnext@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-3.0.1.tgz#e3e4659c4061c09264f6812c33586dc55a009a04"
+  integrity sha512-JpjaJhIN1reaSb26SIxDGtE0uc67gPl19OMVHrr+Ggt6b/Vy60jmCtKgQBrygAH0bhRA2nkxgDvM+8QvR8r0YA==
 
 long@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
   integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
 
-longest@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
-  integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
-
-loose-envify@^1.0.0:
+loose-envify@^1.0.0, loose-envify@^1.2.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
-loud-rejection@^1.0.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
-  integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
-  dependencies:
-    currently-unhandled "^0.4.1"
-    signal-exit "^3.0.0"
-
 lower-case@^1.1.1:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
   integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
 
-lru-cache@^4.0.1, lru-cache@^4.1.5:
+lru-cache@^4.1.2, lru-cache@^4.1.5:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
   integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@@ -3120,16 +4851,40 @@
     pseudomap "^1.0.2"
     yallist "^2.1.2"
 
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+make-dir@^2.0.0, make-dir@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+  integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+  dependencies:
+    pify "^4.0.1"
+    semver "^5.6.0"
+
+make-dir@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
+  integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
+  dependencies:
+    semver "^6.0.0"
+
 map-cache@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
   integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
 
-map-obj@^1.0.0, map-obj@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
-  integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
-
 map-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -3137,11 +4892,6 @@
   dependencies:
     object-visit "^1.0.0"
 
-math-expression-evaluator@^1.2.14:
-  version "1.2.17"
-  resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
-  integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw=
-
 md5.js@^1.3.4:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -3151,12 +4901,22 @@
     inherits "^2.0.1"
     safe-buffer "^5.1.2"
 
+mdn-data@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
+  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+
+mdn-data@2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
+  integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
 
-memory-fs@^0.4.0, memory-fs@~0.4.1:
+memory-fs@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
   integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=
@@ -3164,33 +4924,42 @@
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
-meow@^3.3.0:
-  version "3.7.0"
-  resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
-  integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
+memory-fs@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c"
+  integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==
   dependencies:
-    camelcase-keys "^2.0.0"
-    decamelize "^1.1.2"
-    loud-rejection "^1.0.0"
-    map-obj "^1.0.1"
-    minimist "^1.1.3"
-    normalize-package-data "^2.3.4"
-    object-assign "^4.0.1"
-    read-pkg-up "^1.0.1"
-    redent "^1.0.0"
-    trim-newlines "^1.0.0"
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
 
 merge-descriptors@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
   integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
 
+merge-source-map@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
+  integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==
+  dependencies:
+    source-map "^0.6.1"
+
+merge-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.2.3, merge2@^1.3.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
 methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
   integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
 
-micromatch@^3.1.10, micromatch@^3.1.4:
+micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
   version "3.1.10"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
   integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
@@ -3209,6 +4978,14 @@
     snapdragon "^0.8.1"
     to-regex "^3.0.2"
 
+micromatch@^4.0.0, micromatch@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
+  integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
+  dependencies:
+    braces "^3.0.1"
+    picomatch "^2.0.5"
+
 miller-rabin@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -3217,23 +4994,43 @@
     bn.js "^4.0.0"
     brorand "^1.0.1"
 
-mime-db@1.40.0, "mime-db@>= 1.40.0 < 2":
-  version "1.40.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
-  integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+mime-db@1.44.0, "mime-db@>= 1.43.0 < 2":
+  version "1.44.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
+  integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
 
 mime-types@~2.1.17, mime-types@~2.1.24:
-  version "2.1.24"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
-  integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+  version "2.1.27"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
+  integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
   dependencies:
-    mime-db "1.40.0"
+    mime-db "1.44.0"
 
-mime@1.6.0, mime@^1.5.0:
+mime@1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
 
+mime@^2.4.4:
+  version "2.4.6"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
+  integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
+
+mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mini-css-extract-plugin@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e"
+  integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==
+  dependencies:
+    loader-utils "^1.1.0"
+    normalize-url "1.9.1"
+    schema-utils "^1.0.0"
+    webpack-sources "^1.1.0"
+
 minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -3251,30 +5048,62 @@
   dependencies:
     brace-expansion "^1.1.7"
 
-minimist@0.0.8:
-  version "0.0.8"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+minimist@^1.2.0, minimist@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+  integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
 
-minimist@^1.1.3, minimist@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
-
-minipass@^2.2.1, minipass@^2.3.5:
-  version "2.3.5"
-  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
-  integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+minipass-collect@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
+  integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==
   dependencies:
-    safe-buffer "^5.1.2"
-    yallist "^3.0.0"
+    minipass "^3.0.0"
 
-minizlib@^1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
-  integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+minipass-flush@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
+  integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
   dependencies:
-    minipass "^2.2.1"
+    minipass "^3.0.0"
+
+minipass-pipeline@^1.2.2:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
+  integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
+  dependencies:
+    minipass "^3.0.0"
+
+minipass@^3.0.0, minipass@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
+  integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
+  dependencies:
+    yallist "^4.0.0"
+
+minizlib@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+  integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+  dependencies:
+    minipass "^3.0.0"
+    yallist "^4.0.0"
+
+mississippi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
+  integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
+  dependencies:
+    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@^1.2.0:
   version "1.3.2"
@@ -3284,12 +5113,29 @@
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
-mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
-  version "0.5.1"
-  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
-  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
+  version "0.5.5"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+  integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
   dependencies:
-    minimist "0.0.8"
+    minimist "^1.2.5"
+
+mkdirp@^1.0.3, mkdirp@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+  integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
+move-concurrently@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
+  integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
+  dependencies:
+    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"
 
 ms@2.0.0:
   version "2.0.0"
@@ -3320,9 +5166,14 @@
     thunky "^1.0.2"
 
 nan@^2.12.1:
-  version "2.14.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
-  integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+  version "2.14.1"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
+  integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
+
+nanoid@^2.0.3:
+  version "2.1.11"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
+  integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
 
 nanomatch@^1.2.9:
   version "1.2.13"
@@ -3341,24 +5192,25 @@
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-needle@^2.2.1:
-  version "2.4.0"
-  resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
-  integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
-  dependencies:
-    debug "^3.2.6"
-    iconv-lite "^0.4.4"
-    sax "^1.2.4"
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 
 negotiator@0.6.2:
   version "0.6.2"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
   integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
 
-neo-async@^2.5.0:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
-  integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
+neo-async@^2.5.0, neo-async@^2.6.1:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+  integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
 no-case@^2.2.0:
   version "2.3.2"
@@ -3367,12 +5219,17 @@
   dependencies:
     lower-case "^1.1.1"
 
-node-forge@0.7.5:
-  version "0.7.5"
-  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
-  integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==
+node-fetch@^2.3.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
+  integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
 
-node-libs-browser@^2.0.0:
+node-forge@0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
+  integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
+
+node-libs-browser@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
   integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
@@ -3401,39 +5258,22 @@
     util "^0.11.0"
     vm-browserify "^1.0.1"
 
-node-pre-gyp@^0.12.0:
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
-  integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
-  dependencies:
-    detect-libc "^1.0.2"
-    mkdirp "^0.5.1"
-    needle "^2.2.1"
-    nopt "^4.0.1"
-    npm-packlist "^1.1.6"
-    npmlog "^4.0.2"
-    rc "^1.2.7"
-    rimraf "^2.6.1"
-    semver "^5.3.0"
-    tar "^4"
+node-modules-regexp@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+  integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
 
-nopt@^4.0.1, nopt@~4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
-  integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+node-releases@^1.1.60:
+  version "1.1.60"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084"
+  integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==
+
+nopt@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
+  integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
   dependencies:
     abbrev "1"
-    osenv "^0.1.4"
-
-normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
-  version "2.5.0"
-  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
-  integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
-  dependencies:
-    hosted-git-info "^2.1.4"
-    resolve "^1.10.0"
-    semver "2 || 3 || 4 || 5"
-    validate-npm-package-license "^3.0.1"
 
 normalize-path@^2.1.1:
   version "2.1.1"
@@ -3442,17 +5282,12 @@
   dependencies:
     remove-trailing-separator "^1.0.1"
 
-normalize-path@^3.0.0:
+normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
-normalize-range@^0.1.2:
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
-  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
-
-normalize-url@^1.4.0:
+normalize-url@1.9.1:
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
   integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=
@@ -3462,46 +5297,32 @@
     query-string "^4.1.0"
     sort-keys "^1.0.0"
 
-npm-bundled@^1.0.1:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
-  integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+normalize-url@^3.0.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
+  integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
 
-npm-packlist@^1.1.6:
-  version "1.4.4"
-  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44"
-  integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==
+npm-run-path@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
   dependencies:
-    ignore-walk "^3.0.1"
-    npm-bundled "^1.0.1"
+    path-key "^2.0.0"
 
-npmlog@^4.0.2:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
-  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+npm-run-path@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+  integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
   dependencies:
-    are-we-there-yet "~1.1.2"
-    console-control-strings "~1.1.0"
-    gauge "~2.7.3"
-    set-blocking "~2.0.0"
+    path-key "^3.0.0"
 
-nth-check@~1.0.1:
+nth-check@^1.0.2, nth-check@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
   integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
   dependencies:
     boolbase "~1.0.0"
 
-num2fraction@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
-  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
-
-number-is-nan@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-
 object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -3516,7 +5337,20 @@
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
-object-keys@^1.0.12:
+object-inspect@^1.7.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
+  integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
+
+object-is@^1.0.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6"
+  integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.5"
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
   integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
@@ -3528,6 +5362,24 @@
   dependencies:
     isobject "^3.0.0"
 
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
+object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
+  integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+
 object.pick@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
@@ -3535,6 +5387,16 @@
   dependencies:
     isobject "^3.0.1"
 
+object.values@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
+  integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
 obuf@^1.0.0, obuf@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@@ -3552,21 +5414,53 @@
   resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
   integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
 
-once@^1.3.0:
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
   integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
   dependencies:
     wrappy "1"
 
-opn@^5.1.0:
+onetime@^5.1.0:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+opencollective-postinstall@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
+  integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
+
+opn@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
   integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
   dependencies:
     is-wsl "^1.1.0"
 
-original@>=0.0.5:
+optimize-css-assets-webpack-plugin@^5.0.3:
+  version "5.0.4"
+  resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz#85883c6528aaa02e30bbad9908c92926bb52dc90"
+  integrity sha512-wqd6FdI2a5/FdoiCNNkEvLeA//lHHfG24Ln2Xm2qqdIk4aOlsR18jwpyOihqQ8849W3qu2DX8fOYxpvTMj+93A==
+  dependencies:
+    cssnano "^4.1.10"
+    last-call-webpack-plugin "^3.0.0"
+
+optionator@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+  dependencies:
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+    word-wrap "^1.2.3"
+
+original@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
   integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
@@ -3578,59 +5472,69 @@
   resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
   integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
 
-os-homedir@^1.0.0, os-homedir@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
-  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
-
-os-locale@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
-  integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
-  dependencies:
-    lcid "^1.0.0"
-
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
-  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
-
-osenv@^0.1.4:
-  version "0.1.5"
-  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
-  integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
-  dependencies:
-    os-homedir "^1.0.0"
-    os-tmpdir "^1.0.0"
-
-p-limit@^1.1.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
-  integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
-  dependencies:
-    p-try "^1.0.0"
-
-p-locate@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
-  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
-  dependencies:
-    p-limit "^1.1.0"
-
-p-map@^1.1.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
-  integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
-
-p-try@^1.0.0:
+p-finally@^1.0.0:
   version "1.0.0"
-  resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
-  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+  resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
 
-pako@~1.0.5:
-  version "1.0.10"
-  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
-  integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
+p-limit@^2.0.0, p-limit@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+  integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-locate@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+  integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+  dependencies:
+    p-limit "^2.2.0"
+
+p-map@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
+  integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+
+p-map@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+  integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
+  dependencies:
+    aggregate-error "^3.0.0"
+
+p-retry@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328"
+  integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==
+  dependencies:
+    retry "^0.12.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+pako@~1.0.2, pako@~1.0.5:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+  integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+
+parallel-transform@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
+  integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==
+  dependencies:
+    cyclist "^1.0.1"
+    inherits "^2.0.3"
+    readable-stream "^2.1.5"
 
 param-case@2.1.x:
   version "2.1.1"
@@ -3639,24 +5543,46 @@
   dependencies:
     no-case "^2.2.0"
 
-parse-asn1@^5.0.0:
-  version "5.1.4"
-  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc"
-  integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
   dependencies:
-    asn1.js "^4.0.0"
+    callsites "^3.0.0"
+
+parse-asn1@^5.0.0, parse-asn1@^5.1.5:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
+  integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==
+  dependencies:
+    asn1.js "^5.2.0"
     browserify-aes "^1.0.0"
-    create-hash "^1.1.0"
     evp_bytestokey "^1.0.0"
     pbkdf2 "^3.0.3"
     safe-buffer "^5.1.1"
 
-parse-json@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
-  integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
   dependencies:
-    error-ex "^1.2.0"
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
+parse-json@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"
+  integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+parse-passwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+  integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
 
 parseurl@~1.3.2, parseurl@~1.3.3:
   version "1.3.3"
@@ -3678,28 +5604,36 @@
   resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
   integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
 
-path-exists@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
-  integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
-  dependencies:
-    pinkie-promise "^2.0.0"
-
 path-exists@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
   integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
 
-path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
   integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
 
-path-is-inside@^1.0.1:
+path-is-inside@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
   integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
 
+path-key@^2.0.0, path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-key@^3.0.0, path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
 path-parse@^1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
@@ -3710,19 +5644,15 @@
   resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
   integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
 
-path-type@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
-  integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
-  dependencies:
-    graceful-fs "^4.1.2"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
 pbkdf2@^3.0.3:
-  version "3.0.17"
-  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
-  integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94"
+  integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==
   dependencies:
     create-hash "^1.1.2"
     create-hmac "^1.1.4"
@@ -3730,15 +5660,20 @@
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
+  integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
+
 pify@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
   integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
 
-pify@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
-  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+pify@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+  integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
 
 pinkie-promise@^2.0.0:
   version "2.0.1"
@@ -3752,344 +5687,380 @@
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
   integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
 
-pkg-dir@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
-  integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
+pirates@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+  integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
   dependencies:
-    find-up "^1.0.0"
+    node-modules-regexp "^1.0.0"
 
-pkg-dir@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
-  integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
+pkg-dir@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+  integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
   dependencies:
-    find-up "^2.1.0"
+    find-up "^3.0.0"
 
-portfinder@^1.0.9:
-  version "1.0.21"
-  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.21.tgz#60e1397b95ac170749db70034ece306b9a27e324"
-  integrity sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA==
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+  integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
   dependencies:
-    async "^1.5.2"
-    debug "^2.2.0"
-    mkdirp "0.5.x"
+    find-up "^4.0.0"
+
+please-upgrade-node@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
+  integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==
+  dependencies:
+    semver-compare "^1.0.0"
+
+portfinder@^1.0.26:
+  version "1.0.28"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
+  integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==
+  dependencies:
+    async "^2.6.2"
+    debug "^3.1.1"
+    mkdirp "^0.5.5"
 
 posix-character-classes@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
-postcss-calc@^5.2.0:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
-  integrity sha1-d7rnypKK2FcW4v2kLyYb98HWW14=
+postcss-calc@^7.0.1:
+  version "7.0.4"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.4.tgz#5e177ddb417341e6d4a193c5d9fd8ada79094f8b"
+  integrity sha512-0I79VRAd1UTkaHzY9w83P39YGO/M3bG7/tNLrHGEunBolfoGM0hSjrGvjoeaj0JE/zIw5GsI2KZ0UwDJqv5hjw==
   dependencies:
-    postcss "^5.0.2"
-    postcss-message-helpers "^2.0.0"
-    reduce-css-calc "^1.2.6"
+    postcss "^7.0.27"
+    postcss-selector-parser "^6.0.2"
+    postcss-value-parser "^4.0.2"
 
-postcss-colormin@^2.1.8:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
-  integrity sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=
+postcss-colormin@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381"
+  integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==
   dependencies:
-    colormin "^1.0.5"
-    postcss "^5.0.13"
-    postcss-value-parser "^3.2.3"
+    browserslist "^4.0.0"
+    color "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-convert-values@^2.3.4:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
-  integrity sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=
+postcss-convert-values@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f"
+  integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==
   dependencies:
-    postcss "^5.0.11"
-    postcss-value-parser "^3.1.2"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-discard-comments@^2.0.4:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
-  integrity sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=
+postcss-discard-comments@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033"
+  integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==
   dependencies:
-    postcss "^5.0.14"
+    postcss "^7.0.0"
 
-postcss-discard-duplicates@^2.0.1:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
-  integrity sha1-uavye4isGIFYpesSq8riAmO5GTI=
+postcss-discard-duplicates@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb"
+  integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==
   dependencies:
-    postcss "^5.0.4"
+    postcss "^7.0.0"
 
-postcss-discard-empty@^2.0.1:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
-  integrity sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=
+postcss-discard-empty@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765"
+  integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==
   dependencies:
-    postcss "^5.0.14"
+    postcss "^7.0.0"
 
-postcss-discard-overridden@^0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
-  integrity sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=
+postcss-discard-overridden@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57"
+  integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==
   dependencies:
-    postcss "^5.0.16"
+    postcss "^7.0.0"
 
-postcss-discard-unused@^2.2.1:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
-  integrity sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=
+postcss-merge-longhand@^4.0.11:
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24"
+  integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==
   dependencies:
-    postcss "^5.0.14"
-    uniqs "^2.0.0"
+    css-color-names "0.0.4"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    stylehacks "^4.0.0"
 
-postcss-filter-plugins@^2.0.0:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec"
-  integrity sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==
+postcss-merge-rules@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650"
+  integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==
   dependencies:
-    postcss "^5.0.4"
-
-postcss-load-config@^1.1.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a"
-  integrity sha1-U56a/J3chiASHr+djDZz4M5Q0oo=
-  dependencies:
-    cosmiconfig "^2.1.0"
-    object-assign "^4.1.0"
-    postcss-load-options "^1.2.0"
-    postcss-load-plugins "^2.3.0"
-
-postcss-load-options@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c"
-  integrity sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=
-  dependencies:
-    cosmiconfig "^2.1.0"
-    object-assign "^4.1.0"
-
-postcss-load-plugins@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92"
-  integrity sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=
-  dependencies:
-    cosmiconfig "^2.1.1"
-    object-assign "^4.1.0"
-
-postcss-merge-idents@^2.1.5:
-  version "2.1.7"
-  resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
-  integrity sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=
-  dependencies:
-    has "^1.0.1"
-    postcss "^5.0.10"
-    postcss-value-parser "^3.1.1"
-
-postcss-merge-longhand@^2.0.1:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
-  integrity sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=
-  dependencies:
-    postcss "^5.0.4"
-
-postcss-merge-rules@^2.0.3:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
-  integrity sha1-0d9d+qexrMO+VT8OnhDofGG19yE=
-  dependencies:
-    browserslist "^1.5.2"
-    caniuse-api "^1.5.2"
-    postcss "^5.0.4"
-    postcss-selector-parser "^2.2.2"
+    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"
 
-postcss-message-helpers@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
-  integrity sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=
-
-postcss-minify-font-values@^1.0.2:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
-  integrity sha1-S1jttWZB66fIR0qzUmyv17vey2k=
+postcss-minify-font-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6"
+  integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==
   dependencies:
-    object-assign "^4.0.1"
-    postcss "^5.0.4"
-    postcss-value-parser "^3.0.2"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-minify-gradients@^1.0.1:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
-  integrity sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=
+postcss-minify-gradients@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471"
+  integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==
   dependencies:
-    postcss "^5.0.12"
-    postcss-value-parser "^3.3.0"
+    cssnano-util-get-arguments "^4.0.0"
+    is-color-stop "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-minify-params@^1.0.4:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
-  integrity sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=
+postcss-minify-params@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874"
+  integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==
   dependencies:
-    alphanum-sort "^1.0.1"
-    postcss "^5.0.2"
-    postcss-value-parser "^3.0.2"
+    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"
 
-postcss-minify-selectors@^2.0.4:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
-  integrity sha1-ssapjAByz5G5MtGkllCBFDEXNb8=
+postcss-minify-selectors@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8"
+  integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==
   dependencies:
-    alphanum-sort "^1.0.2"
-    has "^1.0.1"
-    postcss "^5.0.14"
-    postcss-selector-parser "^2.0.0"
+    alphanum-sort "^1.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
 
-postcss-modules-extract-imports@^1.0.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz#dc87e34148ec7eab5f791f7cd5849833375b741a"
-  integrity sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==
+postcss-modules-extract-imports@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
+  integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==
   dependencies:
-    postcss "^6.0.1"
+    postcss "^7.0.5"
 
-postcss-modules-local-by-default@^1.0.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
-  integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
+postcss-modules-local-by-default@^3.0.2:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0"
+  integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==
   dependencies:
-    css-selector-tokenizer "^0.7.0"
-    postcss "^6.0.1"
+    icss-utils "^4.1.1"
+    postcss "^7.0.32"
+    postcss-selector-parser "^6.0.2"
+    postcss-value-parser "^4.1.0"
 
-postcss-modules-scope@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
-  integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
+postcss-modules-scope@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee"
+  integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==
   dependencies:
-    css-selector-tokenizer "^0.7.0"
-    postcss "^6.0.1"
+    postcss "^7.0.6"
+    postcss-selector-parser "^6.0.0"
 
-postcss-modules-values@^1.1.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
-  integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
+postcss-modules-values@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10"
+  integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==
   dependencies:
-    icss-replace-symbols "^1.1.0"
-    postcss "^6.0.1"
+    icss-utils "^4.0.0"
+    postcss "^7.0.6"
 
-postcss-normalize-charset@^1.1.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
-  integrity sha1-757nEhLX/nWceO0WL2HtYrXLk/E=
+postcss-normalize-charset@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4"
+  integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==
   dependencies:
-    postcss "^5.0.5"
+    postcss "^7.0.0"
 
-postcss-normalize-url@^3.0.7:
-  version "3.0.8"
-  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
-  integrity sha1-EI90s/L82viRov+j6kWSJ5/HgiI=
+postcss-normalize-display-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a"
+  integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-positions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f"
+  integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-repeat-style@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c"
+  integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-string@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c"
+  integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==
+  dependencies:
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-timing-functions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9"
+  integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-unicode@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb"
+  integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-url@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1"
+  integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==
   dependencies:
     is-absolute-url "^2.0.0"
-    normalize-url "^1.4.0"
-    postcss "^5.0.14"
-    postcss-value-parser "^3.2.3"
+    normalize-url "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-ordered-values@^2.1.0:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
-  integrity sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=
+postcss-normalize-whitespace@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82"
+  integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==
   dependencies:
-    postcss "^5.0.4"
-    postcss-value-parser "^3.0.1"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-reduce-idents@^2.2.2:
-  version "2.4.0"
-  resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
-  integrity sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=
+postcss-ordered-values@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee"
+  integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==
   dependencies:
-    postcss "^5.0.4"
-    postcss-value-parser "^3.0.2"
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-reduce-initial@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
-  integrity sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=
+postcss-reduce-initial@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
+  integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==
   dependencies:
-    postcss "^5.0.4"
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
 
-postcss-reduce-transforms@^1.0.3:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
-  integrity sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=
+postcss-reduce-transforms@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29"
+  integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==
   dependencies:
-    has "^1.0.1"
-    postcss "^5.0.8"
-    postcss-value-parser "^3.0.1"
+    cssnano-util-get-match "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
 
-postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
-  integrity sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=
+postcss-selector-parser@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270"
+  integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==
   dependencies:
-    flatten "^1.0.2"
+    dot-prop "^5.2.0"
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
-postcss-svgo@^2.1.1:
-  version "2.1.6"
-  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
-  integrity sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=
+postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
   dependencies:
-    is-svg "^2.0.0"
-    postcss "^5.0.14"
-    postcss-value-parser "^3.2.3"
-    svgo "^0.7.0"
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
 
-postcss-unique-selectors@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
-  integrity sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=
+postcss-svgo@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
+  integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==
   dependencies:
-    alphanum-sort "^1.0.1"
-    postcss "^5.0.4"
+    is-svg "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    svgo "^1.0.0"
+
+postcss-unique-selectors@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
+  integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    postcss "^7.0.0"
     uniqs "^2.0.0"
 
-postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+postcss-value-parser@^3.0.0:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
   integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
 
-postcss-zindex@^2.0.1:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
-  integrity sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=
-  dependencies:
-    has "^1.0.1"
-    postcss "^5.0.4"
-    uniqs "^2.0.0"
+postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
+  integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
 
-postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.21, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
-  version "5.2.18"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
-  integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
+  version "7.0.32"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d"
+  integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==
   dependencies:
-    chalk "^1.1.3"
-    js-base64 "^2.1.9"
-    source-map "^0.5.6"
-    supports-color "^3.2.3"
-
-postcss@^6.0.1:
-  version "6.0.23"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
-  integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
-  dependencies:
-    chalk "^2.4.1"
+    chalk "^2.4.2"
     source-map "^0.6.1"
-    supports-color "^5.4.0"
+    supports-color "^6.1.0"
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
 prepend-http@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
   integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
 
+prettier@^1.18.2:
+  version "1.19.1"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
+  integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
+
 pretty-error@^2.0.2:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
@@ -4098,10 +6069,34 @@
     renderkid "^2.0.1"
     utila "~0.4"
 
-private@^0.1.6, private@^0.1.8:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
-  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+pretty-format@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
+  integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==
+  dependencies:
+    "@jest/types" "^25.5.0"
+    ansi-regex "^5.0.0"
+    ansi-styles "^4.0.0"
+    react-is "^16.12.0"
+
+pretty-format@^26.4.2:
+  version "26.4.2"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237"
+  integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==
+  dependencies:
+    "@jest/types" "^26.3.0"
+    ansi-regex "^5.0.0"
+    ansi-styles "^4.0.0"
+    react-is "^16.12.0"
+
+pretty@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5"
+  integrity sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=
+  dependencies:
+    condense-newlines "^0.2.1"
+    extend-shallow "^2.0.1"
+    js-beautify "^1.6.12"
 
 process-nextick-args@~2.0.0:
   version "2.0.1"
@@ -4113,15 +6108,25 @@
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
   integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
 
+progress@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
+promise-inflight@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+  integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
+
 proto-list@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
   integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
 
-protobufjs@^6.8.0:
-  version "6.8.8"
-  resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c"
-  integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==
+protobufjs@^6.10.0:
+  version "6.10.1"
+  resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.1.tgz#e6a484dd8f04b29629e9053344e3970cccf13cd2"
+  integrity sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==
   dependencies:
     "@protobufjs/aspromise" "^1.1.2"
     "@protobufjs/base64" "^1.1.2"
@@ -4133,17 +6138,17 @@
     "@protobufjs/path" "^1.1.2"
     "@protobufjs/pool" "^1.1.0"
     "@protobufjs/utf8" "^1.1.0"
-    "@types/long" "^4.0.0"
-    "@types/node" "^10.1.0"
+    "@types/long" "^4.0.1"
+    "@types/node" "^13.7.0"
     long "^4.0.0"
 
 proxy-addr@~2.0.5:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
-  integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
+  integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
   dependencies:
     forwarded "~0.1.2"
-    ipaddr.js "1.9.0"
+    ipaddr.js "1.9.1"
 
 prr@~1.0.1:
   version "1.0.1"
@@ -4167,6 +6172,31 @@
     randombytes "^2.0.1"
     safe-buffer "^5.1.2"
 
+pump@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+  integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pumpify@^1.3.3:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
+  integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
+  dependencies:
+    duplexify "^3.6.0"
+    inherits "^2.0.3"
+    pump "^2.0.0"
+
 punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -4177,6 +6207,11 @@
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
   integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
 
+punycode@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
 q@^1.1.2:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@@ -4206,11 +6241,11 @@
   integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
 
 querystringify@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
-  integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
+  integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
 
-randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
   integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
@@ -4225,7 +6260,7 @@
     randombytes "^2.0.5"
     safe-buffer "^5.1.0"
 
-range-parser@^1.0.3, range-parser@~1.2.1:
+range-parser@^1.2.1, range-parser@~1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
   integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
@@ -4240,37 +6275,15 @@
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
-rc@^1.2.7:
-  version "1.2.8"
-  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
-  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
-  dependencies:
-    deep-extend "^0.6.0"
-    ini "~1.3.0"
-    minimist "^1.2.0"
-    strip-json-comments "~2.0.1"
+react-is@^16.12.0:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
-read-pkg-up@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
-  integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
-  dependencies:
-    find-up "^1.0.0"
-    read-pkg "^1.0.0"
-
-read-pkg@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
-  integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
-  dependencies:
-    load-json-file "^1.0.0"
-    normalize-package-data "^2.3.2"
-    path-type "^1.0.0"
-
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.3, readable-stream@^2.3.6:
-  version "2.3.6"
-  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
-  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+  version "2.3.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
   dependencies:
     core-util-is "~1.0.0"
     inherits "~2.0.3"
@@ -4280,10 +6293,10 @@
     string_decoder "~1.1.1"
     util-deprecate "~1.0.1"
 
-readable-stream@^3.0.6, readable-stream@^3.1.1:
-  version "3.4.0"
-  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
-  integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
+readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
   dependencies:
     inherits "^2.0.3"
     string_decoder "^1.1.1"
@@ -4298,48 +6311,36 @@
     micromatch "^3.1.10"
     readable-stream "^2.0.2"
 
-redent@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
-  integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
+readdirp@~3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
+  integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
   dependencies:
-    indent-string "^2.1.0"
-    strip-indent "^1.0.1"
+    picomatch "^2.2.1"
 
-reduce-css-calc@^1.2.6:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
-  integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=
+regenerate-unicode-properties@^8.2.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
+  integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
   dependencies:
-    balanced-match "^0.4.2"
-    math-expression-evaluator "^1.2.14"
-    reduce-function-call "^1.0.1"
+    regenerate "^1.4.0"
 
-reduce-function-call@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
-  integrity sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=
+regenerate@^1.4.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f"
+  integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==
+
+regenerator-runtime@^0.13.4:
+  version "0.13.7"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+  integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
+regenerator-transform@^0.14.2:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4"
+  integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==
   dependencies:
-    balanced-match "^0.4.2"
-
-regenerate@^1.2.1:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
-  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
-
-regenerator-runtime@^0.11.0:
-  version "0.11.1"
-  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
-  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
-
-regenerator-transform@^0.10.0:
-  version "0.10.1"
-  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
-  integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==
-  dependencies:
-    babel-runtime "^6.18.0"
-    babel-types "^6.19.0"
-    private "^0.1.6"
+    "@babel/runtime" "^7.8.4"
 
 regex-not@^1.0.0, regex-not@^1.0.2:
   version "1.0.2"
@@ -4349,33 +6350,40 @@
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
-regexpu-core@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
-  integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=
+regexp.prototype.flags@^1.2.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
+  integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
   dependencies:
-    regenerate "^1.2.1"
-    regjsgen "^0.2.0"
-    regjsparser "^0.1.4"
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
 
-regexpu-core@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
-  integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=
+regexpp@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
+  integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+
+regexpu-core@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
+  integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
   dependencies:
-    regenerate "^1.2.1"
-    regjsgen "^0.2.0"
-    regjsparser "^0.1.4"
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.2.0"
+    regjsgen "^0.5.1"
+    regjsparser "^0.6.4"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.2.0"
 
-regjsgen@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
-  integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=
+regjsgen@^0.5.1:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733"
+  integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==
 
-regjsparser@^0.1.4:
-  version "0.1.5"
-  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
-  integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=
+regjsparser@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
+  integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==
   dependencies:
     jsesc "~0.5.0"
 
@@ -4405,32 +6413,20 @@
   resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
   integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
 
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
   integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
 
-repeating@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
-  integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
-  dependencies:
-    is-finite "^1.0.0"
-
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
   integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
 
-require-from-string@^1.1.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
-  integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=
-
-require-main-filename@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
-  integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
 
 requires-port@^1.0.0:
   version "1.0.0"
@@ -4444,42 +6440,90 @@
   dependencies:
     resolve-from "^3.0.0"
 
+resolve-dir@^1.0.0, resolve-dir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
+  integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=
+  dependencies:
+    expand-tilde "^2.0.0"
+    global-modules "^1.0.0"
+
 resolve-from@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
   integrity sha1-six699nWiBvItuZTM17rywoYh0g=
 
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
 resolve-url@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
   integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
 
-resolve@^1.10.0, resolve@^1.3.3:
-  version "1.11.1"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
-  integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
+resolve@^1.3.2:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
+  integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
   dependencies:
     path-parse "^1.0.6"
 
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+
 ret@~0.1.10:
   version "0.1.15"
   resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
   integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
 
-right-align@^0.1.1:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
-  integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8=
-  dependencies:
-    align-text "^0.1.1"
+retry@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
+  integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
 
-rimraf@^2.2.8, rimraf@^2.6.1:
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rgb-regex@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
+  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
+
+rgba-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
+  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
+
+rimraf@2.6.3:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
   integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
   dependencies:
     glob "^7.1.3"
 
+rimraf@^2.5.4, rimraf@^2.6.3:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
 ripemd160@^2.0.0, ripemd160@^2.0.1:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -4488,15 +6532,34 @@
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+run-parallel@^1.1.9:
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
+  integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
+
+run-queue@^1.0.0, run-queue@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
+  integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
+  dependencies:
+    aproba "^1.1.1"
+
+rxjs@^6.6.2:
+  version "6.6.2"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2"
+  integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==
+  dependencies:
+    tslib "^1.9.0"
+
 safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
-safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
-  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
 safe-regex@^1.1.0:
   version "1.1.0"
@@ -4505,39 +6568,75 @@
   dependencies:
     ret "~0.1.10"
 
-"safer-buffer@>= 2.1.2 < 3":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
-sax@^1.2.4, sax@~1.2.1:
+sax@~1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
 
-schema-utils@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
-  integrity sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=
+schema-utils@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+  integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
   dependencies:
-    ajv "^5.0.0"
+    ajv "^6.1.0"
+    ajv-errors "^1.0.0"
+    ajv-keywords "^3.1.0"
+
+schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0, schema-utils@^2.7.1:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
+  integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
+  dependencies:
+    "@types/json-schema" "^7.0.5"
+    ajv "^6.12.4"
+    ajv-keywords "^3.5.2"
 
 select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
   integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
 
-selfsigned@^1.9.1:
-  version "1.10.4"
-  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd"
-  integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==
+selfsigned@^1.10.7:
+  version "1.10.7"
+  resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b"
+  integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==
   dependencies:
-    node-forge "0.7.5"
+    node-forge "0.9.0"
 
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.6.0:
-  version "5.7.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
-  integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
+semver-compare@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+  integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
+
+semver-regex@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338"
+  integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==
+
+semver@7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.0.0, semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+semver@^7.2.1:
+  version "7.3.2"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
+  integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
 
 send@0.17.1:
   version "0.17.1"
@@ -4558,6 +6657,18 @@
     range-parser "~1.2.1"
     statuses "~1.5.0"
 
+serialize-javascript@^1.7.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb"
+  integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==
+
+serialize-javascript@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
+  integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
+  dependencies:
+    randombytes "^2.1.0"
+
 serve-index@^1.9.1:
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
@@ -4581,11 +6692,16 @@
     parseurl "~1.3.3"
     send "0.17.1"
 
-set-blocking@^2.0.0, set-blocking@~2.0.0:
+set-blocking@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
   integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
 
+set-immediate-shim@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+  integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
+
 set-value@^2.0.0, set-value@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
@@ -4619,6 +6735,13 @@
     inherits "^2.0.1"
     safe-buffer "^5.0.1"
 
+shallow-clone@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+  integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+  dependencies:
+    kind-of "^6.0.2"
+
 shebang-command@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -4626,26 +6749,77 @@
   dependencies:
     shebang-regex "^1.0.0"
 
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
 shebang-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
   integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
 
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
 sigmund@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
   integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
 
-signal-exit@^3.0.0:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
-  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
+  integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+  dependencies:
+    is-arrayish "^0.3.1"
 
 slash@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
   integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
 
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slice-ansi@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
+  integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==
+  dependencies:
+    ansi-styles "^3.2.0"
+    astral-regex "^1.0.0"
+    is-fullwidth-code-point "^2.0.0"
+
+slice-ansi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+  integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
+slice-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+  integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -4676,25 +6850,26 @@
     source-map-resolve "^0.5.0"
     use "^3.1.0"
 
-sockjs-client@1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
-  integrity sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=
+sockjs-client@1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
+  integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==
   dependencies:
-    debug "^2.6.6"
-    eventsource "0.1.6"
-    faye-websocket "~0.11.0"
-    inherits "^2.0.1"
+    debug "^3.2.5"
+    eventsource "^1.0.7"
+    faye-websocket "~0.11.1"
+    inherits "^2.0.3"
     json3 "^3.3.2"
-    url-parse "^1.1.8"
+    url-parse "^1.4.3"
 
-sockjs@0.3.19:
-  version "0.3.19"
-  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
-  integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==
+sockjs@0.3.20:
+  version "0.3.20"
+  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855"
+  integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==
   dependencies:
     faye-websocket "^0.10.0"
-    uuid "^3.0.1"
+    uuid "^3.4.0"
+    websocket-driver "0.6.5"
 
 sort-keys@^1.0.0:
   version "1.1.2"
@@ -4703,75 +6878,57 @@
   dependencies:
     is-plain-obj "^1.0.0"
 
-source-list-map@^0.1.4:
-  version "0.1.8"
-  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
-  integrity sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=
-
 source-list-map@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
 
-source-map-resolve@^0.5.0:
-  version "0.5.2"
-  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
-  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+source-map-loader@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.0.tgz#f0fcc88106137793a89ec00f118196b601f111ae"
+  integrity sha512-Kj7rXntLhAsEjZlqGz85Mbnu8N4gcxj5qZI1XyLQjqAI/p92ckRXwErb3jVYL5JxlFJnD4VgwybpB1h6NlETRg==
   dependencies:
-    atob "^2.1.1"
+    abab "^2.0.4"
+    iconv-lite "^0.6.2"
+    loader-utils "^2.0.0"
+    schema-utils "^2.7.0"
+    source-map "^0.6.1"
+    whatwg-mimetype "^2.3.0"
+
+source-map-resolve@^0.5.0:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
+  integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
+  dependencies:
+    atob "^2.1.2"
     decode-uri-component "^0.2.0"
     resolve-url "^0.2.1"
     source-map-url "^0.4.0"
     urix "^0.1.0"
 
-source-map-support@^0.4.15:
-  version "0.4.18"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
-  integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
+source-map-support@^0.5.16, source-map-support@~0.5.12:
+  version "0.5.19"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
+  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
   dependencies:
-    source-map "^0.5.6"
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
 
 source-map-url@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
   integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
 
-source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1:
+source-map@^0.5.6:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
 
-source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-spdx-correct@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
-  integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
-  dependencies:
-    spdx-expression-parse "^3.0.0"
-    spdx-license-ids "^3.0.0"
-
-spdx-exceptions@^2.1.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
-  integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
-
-spdx-expression-parse@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
-  integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
-  dependencies:
-    spdx-exceptions "^2.1.0"
-    spdx-license-ids "^3.0.0"
-
-spdx-license-ids@^3.0.0:
-  version "3.0.5"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
-  integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
-
 spdy-transport@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
@@ -4784,10 +6941,10 @@
     readable-stream "^3.0.6"
     wbuf "^1.7.3"
 
-spdy@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52"
-  integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==
+spdy@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b"
+  integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==
   dependencies:
     debug "^4.1.0"
     handle-thing "^2.0.0"
@@ -4807,6 +6964,30 @@
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
   integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
 
+ssri@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
+  integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
+  dependencies:
+    figgy-pudding "^3.5.1"
+
+ssri@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
+  integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
+  dependencies:
+    minipass "^3.1.1"
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+stackframe@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303"
+  integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==
+
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -4828,6 +7009,14 @@
     inherits "~2.0.1"
     readable-stream "^2.0.2"
 
+stream-each@^1.1.0:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
+  integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
+  dependencies:
+    end-of-stream "^1.1.0"
+    stream-shift "^1.0.0"
+
 stream-http@^2.7.2:
   version "2.8.3"
   resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
@@ -4839,21 +7028,22 @@
     to-arraybuffer "^1.0.0"
     xtend "^4.0.0"
 
+stream-shift@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
+  integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
+
 strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
   integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
 
-string-width@^1.0.1, string-width@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
-  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
-  dependencies:
-    code-point-at "^1.0.0"
-    is-fullwidth-code-point "^1.0.0"
-    strip-ansi "^3.0.0"
+string-argv@0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
+  integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
 
-"string-width@^1.0.2 || 2":
+string-width@^2.0.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
   integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -4861,12 +7051,46 @@
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^4.0.0"
 
-string_decoder@^1.0.0, string_decoder@^1.1.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
-  integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
+string-width@^3.0.0, string-width@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
   dependencies:
-    safe-buffer "~5.1.0"
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string-width@^4.1.0, string-width@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
+  integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.0"
+
+string.prototype.trimend@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
+  integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.5"
+
+string.prototype.trimstart@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
+  integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.5"
+
+string_decoder@^1.0.0, string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
 
 string_decoder@~1.1.1:
   version "1.1.1"
@@ -4875,6 +7099,15 @@
   dependencies:
     safe-buffer "~5.1.0"
 
+stringify-object@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629"
+  integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
+  dependencies:
+    get-own-enumerable-property-symbols "^3.0.0"
+    is-obj "^1.0.1"
+    is-regexp "^1.0.0"
+
 strip-ansi@^3.0.0, strip-ansi@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -4889,109 +7122,192 @@
   dependencies:
     ansi-regex "^3.0.0"
 
-strip-bom@^2.0.0:
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.0"
+
+strip-ansi@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
+  integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
+  dependencies:
+    ansi-regex "^5.0.0"
+
+strip-eof@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+strip-final-newline@^2.0.0:
   version "2.0.0"
-  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
-  integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
-  dependencies:
-    is-utf8 "^0.2.0"
+  resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+  integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
 
-strip-indent@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
-  integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
-  dependencies:
-    get-stdin "^4.0.1"
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
 
-strip-json-comments@~2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
-  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
-
-style-loader@^0.19.0:
-  version "0.19.1"
-  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.1.tgz#591ffc80bcefe268b77c5d9ebc0505d772619f85"
-  integrity sha512-IRE+ijgojrygQi3rsqT0U4dd+UcPCqcVvauZpCnQrGAlEe+FUIyrK93bUDScamesjP08JlQNsFJU+KmPedP5Og==
+style-loader@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a"
+  integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg==
   dependencies:
-    loader-utils "^1.0.2"
-    schema-utils "^0.3.0"
+    loader-utils "^2.0.0"
+    schema-utils "^2.6.6"
+
+stylehacks@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
+  integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
 
 supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
   integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
 
-supports-color@^3.1.0, supports-color@^3.2.3:
-  version "3.2.3"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
-  integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
-  dependencies:
-    has-flag "^1.0.0"
-
-supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
+supports-color@^5.3.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
   integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
   dependencies:
     has-flag "^3.0.0"
 
-svgo@^0.7.0:
-  version "0.7.2"
-  resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
-  integrity sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
   dependencies:
-    coa "~1.0.1"
-    colors "~1.1.2"
-    csso "~2.3.1"
-    js-yaml "~3.7.0"
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^1.0.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
+  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
+  dependencies:
+    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"
-    sax "~1.2.1"
-    whet.extend "~0.9.9"
+    object.values "^1.1.0"
+    sax "~1.2.4"
+    stable "^0.1.8"
+    unquote "~1.1.1"
+    util.promisify "~1.0.0"
 
-tapable@^0.2.7, tapable@~0.2.5:
-  version "0.2.9"
-  resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8"
-  integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==
-
-tar@^4:
-  version "4.4.10"
-  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
-  integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==
+table@^5.2.3:
+  version "5.4.6"
+  resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
+  integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
   dependencies:
-    chownr "^1.1.1"
-    fs-minipass "^1.2.5"
-    minipass "^2.3.5"
-    minizlib "^1.2.1"
-    mkdirp "^0.5.0"
-    safe-buffer "^5.1.2"
-    yallist "^3.0.3"
+    ajv "^6.10.2"
+    lodash "^4.17.14"
+    slice-ansi "^2.1.0"
+    string-width "^3.0.0"
+
+tapable@^1.0.0, tapable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
+  integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
+
+tar@^6.0.2:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f"
+  integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==
+  dependencies:
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    minipass "^3.0.0"
+    minizlib "^2.1.1"
+    mkdirp "^1.0.3"
+    yallist "^4.0.0"
+
+terser-webpack-plugin@^1.4.3:
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
+  integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==
+  dependencies:
+    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"
+
+terser@^4.1.2:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
+  integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
+text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+through2@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+  dependencies:
+    readable-stream "~2.3.6"
+    xtend "~4.0.1"
+
+through@^2.3.8:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
 
 thunky@^1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826"
-  integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==
-
-time-stamp@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.2.0.tgz#917e0a66905688790ec7bbbde04046259af83f57"
-  integrity sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
+  integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
 
 timers-browserify@^2.0.4:
-  version "2.0.10"
-  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae"
-  integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
+  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
   dependencies:
     setimmediate "^1.0.4"
 
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
+
 to-arraybuffer@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
   integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
 
-to-fast-properties@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
-  integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
 
 to-object-path@^0.3.0:
   version "0.3.0"
@@ -5008,6 +7324,13 @@
     is-number "^3.0.0"
     repeat-string "^1.6.1"
 
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
 to-regex@^3.0.1, to-regex@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
@@ -5028,21 +7351,44 @@
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
   integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
 
-trim-newlines@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
-  integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
+ts-loader@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.3.tgz#56858f4296edf1ed55e01f8520552984d3f0911c"
+  integrity sha512-wsqfnVdB7xQiqhqbz2ZPLGHLPZbHVV5Qn/MNFZkCFxRU1miDyxKORucDGxKtsQJ63Rfza0udiUxWF5nHY6bpdQ==
+  dependencies:
+    chalk "^2.3.0"
+    enhanced-resolve "^4.0.0"
+    loader-utils "^1.0.2"
+    micromatch "^4.0.0"
+    semver "^6.0.0"
 
-trim-right@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
-  integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+tslib@^1.9.0, tslib@^1.9.3:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
+  integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
 
 tty-browserify@0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
   integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
 
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+type-fest@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
+  integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
+
+type-fest@^0.8.1:
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
+  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
+
 type-is@~1.6.17, type-is@~1.6.18:
   version "1.6.18"
   resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
@@ -5051,6 +7397,16 @@
     media-typer "0.3.0"
     mime-types "~2.1.24"
 
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
+  integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
+
 uglify-js@3.4.x:
   version "3.4.10"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
@@ -5059,20 +7415,48 @@
     commander "~2.19.0"
     source-map "~0.6.1"
 
-uglify-js@^2.8.27:
-  version "2.8.29"
-  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
-  integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0=
-  dependencies:
-    source-map "~0.5.1"
-    yargs "~3.10.0"
-  optionalDependencies:
-    uglify-to-browserify "~1.0.0"
+uglify-js@^3.6.0:
+  version "3.10.3"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.3.tgz#f0d2f99736c14de46d2d24649ba328be3e71c3bf"
+  integrity sha512-Lh00i69Uf6G74mvYpHCI9KVVXLcHW/xu79YTvH7Mkc9zyKUeSPz0owW0dguj0Scavns3ZOh3wY63J0Zb97Za2g==
 
-uglify-to-browserify@~1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
-  integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc=
+uglifyjs-webpack-plugin@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.2.0.tgz#e75bc80e7f1937f725954c9b4c5a1e967ea9d0d7"
+  integrity sha512-mHSkufBmBuJ+KHQhv5H0MXijtsoA1lynJt1lXOaotja8/I0pR4L9oGaPIZw+bQBOFittXZg9OC1sXSGO9D9ZYg==
+  dependencies:
+    cacache "^12.0.2"
+    find-cache-dir "^2.1.0"
+    is-wsl "^1.1.0"
+    schema-utils "^1.0.0"
+    serialize-javascript "^1.7.0"
+    source-map "^0.6.1"
+    uglify-js "^3.6.0"
+    webpack-sources "^1.4.0"
+    worker-farm "^1.7.0"
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
+  integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
+  integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
 
 union-value@^1.0.0:
   version "1.0.1"
@@ -5094,11 +7478,35 @@
   resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
   integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
 
+unique-filename@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
+  integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
+  dependencies:
+    unique-slug "^2.0.0"
+
+unique-slug@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
+  integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
+  dependencies:
+    imurmurhash "^0.1.4"
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
 unpipe@1.0.0, unpipe@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
   integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
 
+unquote@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
+  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
+
 unset-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
@@ -5108,21 +7516,28 @@
     isobject "^3.0.0"
 
 upath@^1.1.1:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
-  integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
+  integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
 
 upper-case@^1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
   integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
 
+uri-js@^4.2.2:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
+  integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
+  dependencies:
+    punycode "^2.1.0"
+
 urix@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
   integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
 
-url-parse@^1.1.8, url-parse@^1.4.3:
+url-parse@^1.4.3:
   version "1.4.7"
   resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
   integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
@@ -5148,6 +7563,24 @@
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
   integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
 
+util.promisify@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
+  dependencies:
+    define-properties "^1.1.2"
+    object.getownpropertydescriptors "^2.0.3"
+
+util.promisify@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
+  integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.2"
+    has-symbols "^1.0.1"
+    object.getownpropertydescriptors "^2.1.0"
+
 util@0.10.3:
   version "0.10.3"
   resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
@@ -5172,18 +7605,15 @@
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
   integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
 
-uuid@^3.0.1:
-  version "3.3.2"
-  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
-  integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+uuid@^3.3.2, uuid@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+  integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
-validate-npm-package-license@^3.0.1:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
-  integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
-  dependencies:
-    spdx-correct "^3.0.0"
-    spdx-expression-parse "^3.0.0"
+v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
+  integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==
 
 vary@~1.1.2:
   version "1.1.2"
@@ -5191,80 +7621,143 @@
   integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
 
 vendors@^1.0.0:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0"
-  integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
+  integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==
 
 vm-browserify@^1.0.1:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019"
-  integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
+  integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
 
-vue-hot-reload-api@^2.1.0:
-  version "2.3.3"
-  resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
-  integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
-
-vue-loader@^12.1.0:
-  version "12.2.2"
-  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-12.2.2.tgz#2b3a764f27018f975bc78cb8b1f55137548ee2d7"
-  integrity sha512-DD+sYaWQ1esYL/tEwJpoEGE/PFUu32fp7iOuMf4Sra3dgxqr4haTOkVam2VY0/5D4LG8eAcB94ruXKeQW2/ikw==
+vue-clickaway@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/vue-clickaway/-/vue-clickaway-2.2.2.tgz#cecf6839575e8b2afc5d3edb3efb616d293dbb44"
+  integrity sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A==
   dependencies:
-    consolidate "^0.14.0"
+    loose-envify "^1.2.0"
+
+vue-context@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/vue-context/-/vue-context-5.2.0.tgz#d027f626af50e717c2afd57859dec30a851167de"
+  integrity sha512-XH3SwDanAcE7ppzVEkXqpMyzkFKUDp8TDh4vBE9UPbT6OHwLIwtANH6ZAakq8q2iV+hGtDDfwYgX12IbZjyNnw==
+  dependencies:
+    vue-clickaway "^2.2.2"
+
+vue-eslint-parser@^7.0.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.1.0.tgz#9cdbcc823e656b087507a1911732b867ac101e83"
+  integrity sha512-Kr21uPfthDc63nDl27AGQEhtt9VrZ9nkYk/NTftJ2ws9XiJwzJJCnCr3AITQ2jpRMA0XPGDECxYH8E027qMK9Q==
+  dependencies:
+    debug "^4.1.1"
+    eslint-scope "^5.0.0"
+    eslint-visitor-keys "^1.1.0"
+    espree "^6.2.1"
+    esquery "^1.0.1"
+    lodash "^4.17.15"
+
+vue-github-button@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/vue-github-button/-/vue-github-button-1.2.0.tgz#5624a2807b16bdac61589f55503b8a492d2a9d5b"
+  integrity sha512-lvGrbQnHWX8bYovtBFz5uSgtbM+glXXQQq5VR79YDsZ+Fh5DBpLR+Ws5YLz9eSA+gNGi3xnXKjZDbUeQXOYh6A==
+  dependencies:
+    github-buttons "^2.8.0"
+
+vue-github-buttons@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/vue-github-buttons/-/vue-github-buttons-3.1.0.tgz#eaea2ba0b7e0df5a7fd1c61ba37dabf7553dd79a"
+  integrity sha512-x0b9bdhP5xZOD5kQ9+nnCzvKqVyHb4moqN2l06mjYB/k2WRdW5jiAWlneUgoPFwPvcqM40vrTDXVvBrS0MMlEQ==
+  dependencies:
+    format-thousands "^1.1.1"
+    node-fetch "^2.3.0"
+    tslib "^1.9.3"
+
+vue-hot-reload-api@^2.3.0:
+  version "2.3.4"
+  resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
+  integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==
+
+vue-loader@^15.9.3:
+  version "15.9.3"
+  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda"
+  integrity sha512-Y67VnGGgVLH5Voostx8JBZgPQTlDQeOVBLOEsjc2cXbCYBKexSKEpOA56x0YZofoDOTszrLnIShyOX1p9uCEHA==
+  dependencies:
+    "@vue/component-compiler-utils" "^3.1.0"
     hash-sum "^1.0.2"
-    js-beautify "^1.6.3"
     loader-utils "^1.1.0"
-    lru-cache "^4.0.1"
-    postcss "^5.0.21"
-    postcss-load-config "^1.1.0"
-    postcss-selector-parser "^2.0.0"
-    resolve "^1.3.3"
-    source-map "^0.5.6"
-    vue-hot-reload-api "^2.1.0"
-    vue-style-loader "^3.0.0"
-    vue-template-es2015-compiler "^1.2.2"
+    vue-hot-reload-api "^2.3.0"
+    vue-style-loader "^4.1.0"
 
-vue-material@0.8.1:
-  version "0.8.1"
-  resolved "https://registry.yarnpkg.com/vue-material/-/vue-material-0.8.1.tgz#8f54666f52ef07b546ca0da93fbcd71574c599b2"
-  integrity sha512-r7mi6g+VuM/eUvIal4kHRqEzcnK9FIB37QX+9T6ZER66dWOPn+BPs5J1F3k50/QtSovs/qU/dRg1ZhenHU7ISQ==
+vue-material@^1.0.0-beta-11:
+  version "1.0.0-beta-15"
+  resolved "https://registry.yarnpkg.com/vue-material/-/vue-material-1.0.0-beta-15.tgz#949025464f8fe2ff3b9be2ba1365d9eab770ad8a"
+  integrity sha512-nNC1mF1BQNKsyEjRXPYxweYlIOcVE9rK4LeeyppOU6h4vgQnZuNmlGIRnl6fUe8dj+x7c5x5/qydLhJRabPMng==
   dependencies:
-    vue "^2.4.4"
+    opencollective-postinstall "^2.0.2"
+    vue-github-button "^1.2.0"
+    vue-github-buttons "^3.1.0"
+    vue-toc "0.0.1"
 
-vue-style-loader@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.1.2.tgz#6b66ad34998fc9520c2f1e4d5fa4091641c1597a"
-  integrity sha512-ICtVdK/p+qXWpdSs2alWtsXt9YnDoYjQe0w5616j9+/EhjoxZkbun34uWgsMFnC1MhrMMwaWiImz3K2jK1Yp2Q==
+vue-router@^3.0:
+  version "3.4.5"
+  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.5.tgz#d396ec037b35931bdd1e9b7edd86f9788dc15175"
+  integrity sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg==
+
+vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"
+  integrity sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==
   dependencies:
     hash-sum "^1.0.2"
     loader-utils "^1.0.2"
 
-vue-template-compiler@^2.3.3:
-  version "2.6.10"
-  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc"
-  integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==
+vue-template-compiler@^2.6.11:
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
+  integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==
   dependencies:
     de-indent "^1.0.2"
     he "^1.1.0"
 
-vue-template-es2015-compiler@^1.2.2:
+vue-template-es2015-compiler@^1.9.0:
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
 
-vue@^2.3.3, vue@^2.4.4:
-  version "2.6.10"
-  resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
-  integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
-
-watchpack@^1.3.1:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
-  integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==
+vue-toc@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/vue-toc/-/vue-toc-0.0.1.tgz#6a4dfa9c144445679705cd7b991a1a73ac080cc0"
+  integrity sha512-RZfVgLzk/kpEmk05ptvU/+x3TVo4Ai4BBARvV4iCurR9bJsAqnnrqwjEBKnEG+s6NT0yQ6EY0JMGViyOUGysDw==
   dependencies:
-    chokidar "^2.0.2"
+    vue "^2.6.10"
+
+vue@^2.3.3, vue@^2.6.10:
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
+  integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
+
+vuex@^3.0, vuex@^3.4.0:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d"
+  integrity sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw==
+
+watchpack-chokidar2@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
+  integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==
+  dependencies:
+    chokidar "^2.1.8"
+
+watchpack@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b"
+  integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==
+  dependencies:
     graceful-fs "^4.1.2"
     neo-async "^2.5.0"
+  optionalDependencies:
+    chokidar "^3.4.1"
+    watchpack-chokidar2 "^2.0.0"
 
 wbuf@^1.1.0, wbuf@^1.7.3:
   version "1.7.3"
@@ -5273,198 +7766,289 @@
   dependencies:
     minimalistic-assert "^1.0.0"
 
-webpack-dev-middleware@1.12.2:
-  version "1.12.2"
-  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
-  integrity sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==
+webpack-cli@^3.3.12:
+  version "3.3.12"
+  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a"
+  integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==
   dependencies:
-    memory-fs "~0.4.1"
-    mime "^1.5.0"
-    path-is-absolute "^1.0.0"
-    range-parser "^1.0.3"
-    time-stamp "^2.0.0"
+    chalk "^2.4.2"
+    cross-spawn "^6.0.5"
+    enhanced-resolve "^4.1.1"
+    findup-sync "^3.0.0"
+    global-modules "^2.0.0"
+    import-local "^2.0.0"
+    interpret "^1.4.0"
+    loader-utils "^1.4.0"
+    supports-color "^6.1.0"
+    v8-compile-cache "^2.1.1"
+    yargs "^13.3.2"
 
-webpack-dev-server@^2.4.5:
-  version "2.11.5"
-  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz#416fbdea0e04eebe44a626e791d5a2eb37fe8c48"
-  integrity sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==
+webpack-dev-middleware@^3.7.2:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
+  integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==
+  dependencies:
+    memory-fs "^0.4.1"
+    mime "^2.4.4"
+    mkdirp "^0.5.1"
+    range-parser "^1.2.1"
+    webpack-log "^2.0.0"
+
+webpack-dev-server@^3.11.0:
+  version "3.11.0"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c"
+  integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==
   dependencies:
     ansi-html "0.0.7"
-    array-includes "^3.0.3"
     bonjour "^3.5.0"
-    chokidar "^2.1.2"
-    compression "^1.7.3"
-    connect-history-api-fallback "^1.3.0"
-    debug "^3.1.0"
-    del "^3.0.0"
-    express "^4.16.2"
-    html-entities "^1.2.0"
-    http-proxy-middleware "^0.19.1"
-    import-local "^1.0.0"
-    internal-ip "1.2.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"
-    killable "^1.0.0"
-    loglevel "^1.4.1"
-    opn "^5.1.0"
-    portfinder "^1.0.9"
-    selfsigned "^1.9.1"
+    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.19"
-    sockjs-client "1.1.5"
-    spdy "^4.0.0"
-    strip-ansi "^3.0.0"
-    supports-color "^5.1.0"
-    webpack-dev-middleware "1.12.2"
-    yargs "6.6.0"
+    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"
 
-webpack-sources@^1.0.1:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
-  integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==
+webpack-log@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
+  integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==
+  dependencies:
+    ansi-colors "^3.0.0"
+    uuid "^3.3.2"
+
+webpack-log@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-3.0.1.tgz#647c42231b6f74d7cc3c3a66510370e635d066ea"
+  integrity sha512-mX/6BJPPpxco6BGCFZJ96NjgnwBrLQx6d7Kxe1PaJ7KvjI3LFmJK9QgRPCAr9tXrPVawPN1cuM8hJ2Vadnwm+Q==
+  dependencies:
+    chalk "^2.4.2"
+    loglevelnext "^3.0.1"
+    nanoid "^2.0.3"
+
+webpack-merge@^5.0.9:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.1.3.tgz#e5570b0bfa654915340f1e348c93a6fd16d7a820"
+  integrity sha512-fz/xHgfHyxq3uzGGrMryPnpPZ6x3vF1tHtws6vYwYX+8e6Dw+4U4r6rXuEPCqtSwmUIeD8hniWwFem+5FVLjzg==
+  dependencies:
+    clone-deep "^4.0.1"
+    wildcard "^2.0.0"
+
+webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
+  integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
   dependencies:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
-webpack@^2.6.1:
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.7.0.tgz#b2a1226804373ffd3d03ea9c6bd525067034f6b1"
-  integrity sha512-MjAA0ZqO1ba7ZQJRnoCdbM56mmFpipOPUv/vQpwwfSI42p5PVDdoiuK2AL2FwFUVgT859Jr43bFZXRg/LNsqvg==
+webpack@^4.43.0:
+  version "4.44.1"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21"
+  integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==
   dependencies:
-    acorn "^5.0.0"
-    acorn-dynamic-import "^2.0.0"
-    ajv "^4.7.0"
-    ajv-keywords "^1.1.1"
-    async "^2.1.2"
-    enhanced-resolve "^3.3.0"
-    interpret "^1.0.0"
-    json-loader "^0.5.4"
-    json5 "^0.5.1"
-    loader-runner "^2.3.0"
-    loader-utils "^0.2.16"
-    memory-fs "~0.4.1"
-    mkdirp "~0.5.0"
-    node-libs-browser "^2.0.0"
-    source-map "^0.5.3"
-    supports-color "^3.1.0"
-    tapable "~0.2.5"
-    uglify-js "^2.8.27"
-    watchpack "^1.3.1"
-    webpack-sources "^1.0.1"
-    yargs "^6.0.0"
+    "@webassemblyjs/ast" "1.9.0"
+    "@webassemblyjs/helper-module-context" "1.9.0"
+    "@webassemblyjs/wasm-edit" "1.9.0"
+    "@webassemblyjs/wasm-parser" "1.9.0"
+    acorn "^6.4.1"
+    ajv "^6.10.2"
+    ajv-keywords "^3.4.1"
+    chrome-trace-event "^1.0.2"
+    enhanced-resolve "^4.3.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.3"
+    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.7.4"
+    webpack-sources "^1.4.1"
+
+websocket-driver@0.6.5:
+  version "0.6.5"
+  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
+  integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=
+  dependencies:
+    websocket-extensions ">=0.1.1"
 
 websocket-driver@>=0.5.1:
-  version "0.7.3"
-  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
-  integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
+  integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
   dependencies:
-    http-parser-js ">=0.4.0 <0.4.11"
+    http-parser-js ">=0.5.1"
     safe-buffer ">=5.1.0"
     websocket-extensions ">=0.1.1"
 
 websocket-extensions@>=0.1.1:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
-  integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
+  integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
 
-whet.extend@~0.9.9:
-  version "0.9.9"
-  resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
-  integrity sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=
+whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
 
-which-module@^1.0.0:
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which-pm-runs@^1.0.0:
   version "1.0.0"
-  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-  integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
+  resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
+  integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
 
-which@^1.2.9:
+which@^1.2.14, which@^1.2.9, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
   dependencies:
     isexe "^2.0.0"
 
-wide-align@^1.1.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
-  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
   dependencies:
-    string-width "^1.0.2 || 2"
+    isexe "^2.0.0"
 
-window-size@0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
-  integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=
+wildcard@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
+  integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
 
-wordwrap@0.0.2:
-  version "0.0.2"
-  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
-  integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=
+word-wrap@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
 
-wrap-ansi@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
-  integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
+worker-farm@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
+  integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
   dependencies:
-    string-width "^1.0.1"
-    strip-ansi "^3.0.1"
+    errno "~0.1.7"
+
+wrap-ansi@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+  integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+  dependencies:
+    ansi-styles "^3.2.0"
+    string-width "^3.0.0"
+    strip-ansi "^5.0.0"
+
+wrap-ansi@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
 
 wrappy@1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
 
-xtend@^4.0.0:
+write@1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
+  integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
+  dependencies:
+    mkdirp "^0.5.1"
+
+ws@^6.2.1:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+  integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+xtend@^4.0.0, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
   integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
 
-y18n@^3.2.1:
-  version "3.2.1"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
-  integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
+y18n@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
 
 yallist@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
   integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
 
-yallist@^3.0.0, yallist@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
-  integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
+yallist@^3.0.2:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
 
-yargs-parser@^4.2.0:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
-  integrity sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=
-  dependencies:
-    camelcase "^3.0.0"
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yargs@6.6.0, yargs@^6.0.0:
-  version "6.6.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
-  integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=
+yaml@^1.10.0, yaml@^1.7.2:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
+  integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
+
+yargs-parser@^13.1.2:
+  version "13.1.2"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
+  integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
   dependencies:
-    camelcase "^3.0.0"
-    cliui "^3.2.0"
-    decamelize "^1.1.1"
-    get-caller-file "^1.0.1"
-    os-locale "^1.4.0"
-    read-pkg-up "^1.0.1"
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs@^13.3.2:
+  version "13.3.2"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+  integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
+  dependencies:
+    cliui "^5.0.0"
+    find-up "^3.0.0"
+    get-caller-file "^2.0.1"
     require-directory "^2.1.1"
-    require-main-filename "^1.0.1"
+    require-main-filename "^2.0.0"
     set-blocking "^2.0.0"
-    string-width "^1.0.2"
-    which-module "^1.0.0"
-    y18n "^3.2.1"
-    yargs-parser "^4.2.0"
-
-yargs@~3.10.0:
-  version "3.10.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
-  integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=
-  dependencies:
-    camelcase "^1.0.2"
-    cliui "^2.1.0"
-    decamelize "^1.0.0"
-    window-size "0.1.0"
+    string-width "^3.0.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^13.1.2"